Diseño de implementaciones de solicitudes de búsqueda intercambiables: revisión del código, parte 4

Índice
  1. Explorando las necesidades
  2. Explorando el concepto arquitectónico
    1. ¿Por qué es importante reutilizar?
    2. Explorando cómo hacerlo reutilizable
  3. Inyección de diferentes implementaciones
    1. Paso 1: Definición del contrato
    2. Paso 2: Implementación del generador de publicaciones
    3. Paso 3: Conecte el contrato a FilterWPQuery
    4. Paso 4: Vincular la implementación al iniciar el complemento
    5. Paso 5: Pasar la consulta
  4. Vamos a repasar
  5. Vamos a discutirlo

En elúltimo artículo, identificamos que la tarea de generar publicaciones, aunque arbitraria, es solo una posible implementación de la obtención de contenido para una consulta de búsqueda. Por lo tanto, dividimos las tareas de “obtener contenido” y “generar publicaciones” en métodos separados. Este diseño nos prepara para una discusión sobre el diseño de implementaciones de solicitudes de búsqueda intercambiables.

Antes de empezar, quiero recordarles queJosh diseñó este complementocon fines educativos para enseñarte programación orientada a objetos avanzada y pruebas. A medida que tú y yo trabajamos juntos en la revisión de código, las tareas de “obtener contenido” se revelan como una oportunidad perfecta para profundizar en la programación orientada a objetos explorando cómo diseñar para la reutilización.

Comencemos con la necesidad y luego avancemos hacia los conceptos de diseño.

Explorando las necesidades

Piense en una aplicación de procesamiento de búsquedas del mundo real. Es probable que cada proyecto requiera distintas necesidades para obtener el contenido de la búsqueda.

Es posible que un proyecto deba realizar algunas tareas de obtención y procesamiento antes de devolverlo. Otro proyecto puede necesitar realizar algunas tareas de clasificación y ensamblaje. De un proyecto a otro, los detalles de cómo manejar la “obtención de contenido” pueden ser diferentes según el mapeo de contenido del sitio y las necesidades comerciales.

¿Cómo podemos hacer que la FilterWPQueryclase sea más flexible para adaptarse a estas diferentes necesidades del proyecto sin aumentar nuestros costos o riesgos?

Hagamos una lluvia de ideas sobre nuestros objetivos de diseño para satisfacer nuestras necesidades:

  1. Queremos construirlo FilterWPQueryuna vez y luego reutilizarlo una y otra vez en cada proyecto.
  2. Queremos una forma de aislar las necesidades comerciales de nuestro proyecto para “obtener contenido” para la consulta de búsqueda.
  3. Queremos que esas “necesidades” sean implementaciones intercambiables.
  4. Queremos reducir nuestros costes y riesgos.

Explorando el concepto arquitectónico

Quiero que imagines este concepto arquitectónico en tu mente. Imagina que el FilterWPQuery::getPosts()método puede llamar a un método estandarizado en un $contentGetterobjeto genérico. Imagina que este objeto genérico representa la implementación del proyecto, lo que significa que podemos intercambiar implementaciones de un proyecto a otro.

Piense en el poder de este concepto.

  1. FilterWPQuery::getPosts()Al método no le importa cuál es la implementación; en cambio, simplemente le dice a este objeto genérico: “Oye, dame el contenido de la búsqueda”.
  2. Utiliza un método estandarizado para indicar el objeto, es decir, llamemos a este método getContent().
  3. Cada implementación tiene este método estandarizado, que internamente se conecta para manejar sus propias tareas para “obtener el contenido”.

Este diseño lo hace FilterWPQuery()reutilizable.

¿Qué más hace? Piénsalo.

Permite crear una nueva implementación para cada proyecto y luego conectarla a FilterWPQuery(). Crea un diseño intercambiable y flexible.

Espere, hay otra ventaja: proporciona un mecanismo para reducir los costos y los riesgos. Creo que debemos hablar sobre por qué esto es importante.

¿Por qué es importante reutilizar?

Es hora de debatir brevemente por qué la reutilización es importante para su equipo y su negocio.

Imagínese si tuviera que codificar de forma rígida cada implementación en FilterWPQuery::getPosts(). ¿Qué sucede en el próximo proyecto? Debe cambiar la implementación. Eso significa que está modificando manualmente la FilterWPQuery::getPosts()clase. Eso parece bastante inocente, pero tiene costos y riesgos ocultos. ¿Por qué?

La clase tiene otras funciones básicas incorporadas, como conectarse y desconectarse de WordPress, así como decidir si filtrar o no la solicitud de búsqueda.

Déjame hacerte una pregunta.

¿Qué sucede más adelante cuando encuentras un error o algo cambia y requieres un cambio en este código base? Piensa en los proyectos que ya has enviado con este mismo código.

Si se produce un cambio, deberá actualizar todos los demás proyectos. Pero si cada clase es diferente, no puede aplicar un cambio para solucionarlos todos. Vaya. Eso significa que debe actualizar manualmente cada proyecto. Eso lleva mucho tiempo.

Pero este enfoque manual tiene otro problema costoso: corregir errores manualmente en varios proyectos aumenta la probabilidad de cometer un error tipográfico y generar otro.

Codificar de forma rígida la implementación de cada proyecto para obtener contenido es una mala estrategia. A largo plazo, le costará dinero a su empresa respaldar y mantener todos sus proyectos.

Una mejor estrategia es aislar las personalizaciones entre proyectos, diseñando su base de código para que sea flexible y reutilizable y al mismo tiempo brinde la capacidad de intercambiar diferentes implementaciones.

Explorando cómo hacerlo reutilizable

¿Cómo podemos FilterWPQuery::getPosts()llamar a un objeto genérico? Podemos aprovechar el poder y la flexibilidad depolimorfismo.

Quédate conmigo mientras te guío a través de un experimento mental.

Imaginemos que existe una forma de establecer un estándar estricto que defina cómo interactuar con múltiples implementaciones. Para que esto sea reutilizable, necesitaríamos una forma de definir estrictamente cada método para la interacción y luego obligar a cada implementación a implementar el método o los métodos.

¿Sigues conmigo? Creo que para ayudarte a visualizar el diseño, debemos volver a nuestro código.

Digamos que queremos FilterWPQuery::getPosts()que nuestro siempre llame al mismo método independientemente de qué implementación esté conectada a él. Llamemos a este método estándar getContent(). Entonces, cada implementación en cada proyecto tendrá un getContent()método. Internamente, cada implementación maneja su propio trabajo personalizado para “obtener el contenido”.

Pero espere un momento. ¿Cómo forzamos esta estandarización estricta? En PHP OOP, usamos uninterfaz.

¿Qué es una interfaz?Una interfaz es un contrato que define cómo interactuar con cada objeto que lo implementa.Este contrato establece un estándar estricto.

Una interfaz es un contrato que define cómo interactuar con cada objeto que lo implementa estableciendo un estándar estricto.

Conceptualmente, la interfaz es el pegamento que une nuestro código, lo que nos permite aislar implementaciones personalizadas específicas del proyecto y reutilizar la FilterWPQueryclase una y otra vez.

Es hora de mirar algo de código.

Inyección de diferentes implementaciones

Repasemos el proceso de refactorización del código para que sea más reutilizable según lo que hemos estado comentando en este artículo. Necesitaremos:

  1. Añade el contrato.
  2. Hacer del generador de publicaciones una implementación.
  3. Conectar el contrato:
    • Inicializar FilterWPQueryy conectar la implementación del proyecto.
    • Cambiar getPosts()para llamar a la implementación a través del contrato.
  4. Inyectar la implementación al iniciar el complemento.

Paso 1: Definición del contrato

Comencemos construyendo el contrato:

?phpespacio de nombres CalderaLearnRestSearchContentGetter;/** * Define el contrato para cada implementación de captador de contenido. * @paquete CalderaLearnRestSearchContentGetter */Interfaz ContentGetterContract{/** * Maneja la obtención del contenido para la consulta de búsqueda. * * @param int $quantity Número a obtener. * * @return matriz */función pública getContent( $cantidad = 4 ): matriz;}

Tenga en cuenta que nuestro contrato tiene un método getContent()y este método acepta la cantidad y devuelve una matriz de contenido.

Paso 2: Implementación del generador de publicaciones

Utilicemos este contrato y construyamos el generador de publicaciones como una implementación independiente. Para ello, implementamos el contrato, agregamos el método y luego movemos el código del generador de publicaciones a FilterWPQueryesta nueva clase.

?phpespacio de nombres CalderaLearnRestSearchContentGetter;utilizar stdClass;utiliza WP_Post;/** * Maneja la generación de publicaciones para la consulta de búsqueda. * @paquete CalderaLearnRestSearchContentGetter */La clase PostsGenerator implementa ContentGetterContract{/** * Maneja la obtención del contenido para la consulta de búsqueda. * * @param int $quantity Número a obtener. * * @return matriz */función pública getContent($cantidad = 4): matriz{devuelve $this-generatePosts($cantidad);}/** * Genera una matriz de publicaciones simuladas. * * @param int $quantity Número de publicaciones a generar. * * @return matriz */función privada generatePosts($cantidad): matriz{$Publicaciones simuladas = [];para ($postNumber = 0; $postNumber  $cantidad; $postNumber++) {$post = nuevo WP_Post( nueva stdClass() );$post-post_title = "Publicación simulada {$postNumber}";$post-filter = 'sin procesar';$mockPosts[] = $publicación;}devuelve $mockPosts;}}

Paso 3: Conecte el contrato a FilterWPQuery

A continuación, debemos conectar la implementación a la consulta de búsqueda:

?phpespacio de nombres CalderaLearnRestSearch;utilice CalderaLearnRestSearchContentGetterContentGetterContract;/** * Clase FilterWPQuery * * Cambia el objeto WP_Query durante las solicitudes de API REST * * @paquete CalderaLearnRestSearch */La clase FilterWPQuery implementa FiltersPreWPQuery{/** * Implementación de Content Getter. * * @var Contrato de obtención de contenido */estático protegido $contentGetter;// Código omitido por brevedad/** * Inicializar el filtro de búsqueda vinculando una implementación de captador de contenido específico. * * @param ContentGetterContract $contentGetter Instancia de la implementación. * * @return nulo */función pública estática init(ContentGetterContract $contentGetter){estático::$contentGetter = $contentGetter;}/** @hereditardoc */función estática pública getPosts(): matriz{devolver estático::$contentGetter-getContent();}}

Déjame explicarte lo que está pasando aquí:

  1. Especificamos una propiedad estática llamada $contentGetterpara contener la implementación. Este es nuestro objeto de obtención de contenido genérico.
  2. Agregamos un método de inicialización llamado init()e inyectamos la implementación del captador de contenido específico del proyecto.
  3. Utilizamos esta propiedad genérica $contentGettery luego invocamos el método estándar getContent().

Ten en cuenta que escribimos hint con el contrato y no con el nombre de la clase de implementación. ¿Por qué? Queremos que el captador de contenido sea genérico, de modo que FilterWPQueryno tenga en cuenta qué implementación uses. ¿Eh?

Todas las implementaciones implementan el contrato. ¿Correcto? ¿Qué tienen en común todas las implementaciones? El contrato.

En PHP, podemos escribir una pista a través del contrato. Eso nos permite inyectar cualquiera de las implementaciones. ¿Tiene sentido? Si no, pregúntame en los comentarios a continuación.

Paso 4: Vincular la implementación al iniciar el complemento

Por último, debemos vincular la implementación de nuestro proyecto a FilterWPQuery. Hagámoslo al iniciar el complemento en el archivo de arranque del complemento:

?php// Código omitido por brevedad/** * Ejecutar el complemento. */agregar_accion( 'init', función(){FilterWPQuery::init( nuevo PostsGenerator() );( nuevos Hooks() )-addHooks();});

Paso 5: Pasar la consulta

Hasta ahora, hemos limitado la implementación para que solo sepa cuántos fragmentos de contenido crear. ¿Qué sucede si se necesita más información? Para una aplicación del mundo real, querríamos obtener los parámetros adicionales de la consulta de búsqueda en lugar de codificar una cantidad predeterminada.

Para lograrlo, necesitaremos modificar nuestro código para que acepte dos argumentos de la consulta, pasar la consulta a la implementación y luego compilar la implementación para extraer lo que necesita. Hay bastantes cambios para que esto funcione. Para verlos, consulteSolicitud de extracción n.° 6 en GitHub.

Vamos a repasar

En este artículo, usted y yo recorrimos un experimento mental sobre cómo pensar en el diseño de una arquitectura flexible mediante:

  • Separar las implementaciones personalizadas.
  • Sin FilterWPQueryimportar qué implementación se utiliza.

Aprendiste sobre:

  • Las interfaces PHP son un contrato entre un llamador y cada implementación.
  • Un contrato define la interfaz estricta de cómo interactuar con una implementación.
  • Cómo aprovechar el polimorfismo inyectando la implementación que su proyecto necesita.

Realizamos este ejercicio para desarrollar tu forma de pensar sobre la creación de código para su reutilización. Al diseñar implementaciones independientes para las partes del código que probablemente cambiarán de un proyecto a otro, estás diseñando para su reutilización. Luego, aprovechas estas implementaciones intercambiables.

El resultado es que usted reduce sus costos y riesgos al tiempo que hace que sus proyectos sean más flexibles.

El código final y cada paso de refactorización están documentados en elSolicitud de extracción n.° 5 en GitHubTe invito a explorarlo.

Vamos a discutirlo

¿Qué opinas? No, en serio, quiero saber qué piensas. Te invito a que hagas preguntas, compartas tu opinión o compartas tus ideas en los comentarios a continuación.

¿Ves las ventajas de diseñar para reutilizar? A través de este experimento mental y guía de refactorización, ¿tiene sentido separar las implementaciones de las partes reutilizables de tu base de código?

Espero poder hablar con usted sobre la reutilización.

SUSCRÍBETE A NUESTRO BOLETÍN 
No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion