Normalmente nos enfrentamos a problemas que seguro tienen alguna solución en stackoverflow.com, pero ¿buscamos siempre si alguien pasó por ese problema o similar antes? Lo mismo ocurre con los patrones. Ponernos a tirar código a lo loco, incluso haciendo TDD puede llevarnos a soluciones que funcionen pero que no sean del todo finas. Por ello creo que es necesario tener un conocimiento amplio sobre patrones. Cuando aplicarlos, porqué aplicarlos, qué podamos identificar cuando estamos cerca de un patrón y así poder utilizarlo para cuando un compañero (o nuestro yo del futuro) lo pille a la primera.
Esta es una adaptación de The Whens and Whys for PHP Design Patterns donde veremos cuando y por qué tenemos que aplicar ciertos patrones de diseño.
Factory pattern
El patrón factoría se inventó para ayudar a los programadores a organizar la información relacionada con crear un objeto. Los objetos a veces tienen ciertos parámetros en el constructor; otras veces, tienen información predeterminada en su creación. Estos objetos pueden crearse mediante «factorías», para así mantener toda la información con respecto a su creación en un solo lugar.
Cuando: Se usará factory pattern cuando se necesite reunir la información para crear objetos.
Por qué Las factorías ayudan a contener la lógica de creación de objetos en un solo lugar. También pueden romper dependencias para facilitar un débil acoplamiento y permitir inyección de dependencias para mejorar el testing.
En el repositorio de Github DesignPatternsPHP podemos encontrar ejemplos de factorías, como SimpleFactory, StaticFactory o AbstractFactory
The Gateway Pattern
Este patrón define un canal de comunicación entre una solución de persistencia y la lógica de negocio. Para aplicaciones simples, se pueden recuperar o crear objetos enteros por sí mismos, en cambio, para aplicaciones complejas esta creación/recuperación es responsabilidad de las factorías. Gateway Pattern simplemente recupera y persisten datos en bruto.
Cuando Cuando se necesita recuperar o mantener información
Por qué Ofrece una interfaz pública simple para operaciones de persistencias. También encapsula el conocimiento de la persistencia y de la misma manera se desacopla la lógica y negocio de la lógica de persistencia.
Aquí podemos ver el ejemplo de Table Data Gateway de Martin Fowler.
Proxy Pattern
Hay momentos en los que no se puede / no se quiere exponer el conocimiento de la capa de persistencia a las clases de negocio. El patrón _proxy es una buena solución para hacer esto.
Cuando Cuando es necesario recuperar información de la capa de persistencia o de una fuente externa, pero no se quiere que la lógica de negocio sepa de donde viene la información.
Por qué Para ofrecer un enfoque no invasivo a la creación de objetos. También se abre la posibilidad de recuperar los objetos sobre la marcha, solo cuando sea necesario y de diferentes fuentes.
Un proxy implementa la misma interfaz que un objeto real e imita su funcionalidad. La lógica de negocio simplemente lo utiliza como si fuera un objeto real, pero de hecho, el proxy crea el objeto si no existe uno.
Este patrón es muy utilizado, por ejemplo Doctrine2. Podemos ver en este repositorio un ejemplo de uso.
Repository Pattern
El patrón repositorio es muy útil para implementar métodos de búsqueda y «lenguajes de consulta». Estas consultas se necesitan usar el gateway para obtener los objetos que se necesitan producidos mediante la factoría
El patrón repositorio es muy usado en Domain Driven Design (DDD)
Cuando Es necesario crear múltiples objetos en función de criterios de búsqueda, o cuando se necesitan guardar varios objetos en la capa de persistencia
Por qué Para permitir que los clientes que necesitan objetos específicos para trabajar con una consulta y un lenguaje de persistencia. Se elimina el aún más el código relacionado con la creación en la lógica de negocio.
Pero ¿qué pasa si el repositorio no puede encontrar los objetos? Una opción podría ser devolver el valor NULL. Esto obliga a incluir numerosos checks if(is_null($param)) return ;
en el código.
Un mejor enfoque es devolver un objeto nulo.
Aquí tenemos un ejemplo de repository pattern.
The Null Object Pattern
Un objeto nulo implementa la misma interfaz que otros objetos, pero los objetos devuelven valores «neutrales» Por ejemplo un método que devuelve un string podría devolver un string vacío; otro método que devuelva un valor numérico podría devolver 0. Esto obliga a tener métodos que no devuelven datos significativos, pero se pueden utilizar estos objetos sin tener que preocuparse de verificar nulos.
Cuando Cuando se verifica frecuentemente si un campo/objeto es nulo.
Por qué Esto puede añadir claridad al código y obliga a pensar un poco más sobre el comportamiento de los objetos.
No es inusual llamar a muchos métodos en un objeto antes de que este pueda empezar a hacer su trabajo. Hay situaciones en las que se debe preparar un objeto después de su creación, antes de que realmente pueda utilizarse. Esto conduce a que haya duplicidad de código al crear esos objetos en diferentes lugares.
Aquí tenemos un ejemplo del patrón.
Command pattern
Cuando Cuando es necesario realizar muchas operaciones para preparar los objetos
Por qué Para mover la complejidad del código desde la consumición a la creación.
Esto suena bien, ¿no? De hecho, es útil en muchas situaciones. El patrón «Command» es ampliamente utilizado en transacciones. Se añade un método undo()
es posible realizar un seguimiento de todas las operaciones de un objeto «command» y revertirlas si es necesario.
Aquí tenemos un ejemplo del patrón.
Active Object
El objeto activo solo tiene una responsabilidad: contener una lista de objetos «command» y ejecutarlos
Cuando Varios objetos similares tienen que ejecutar un solo comando
Por qué Obliga a los clientes a realizar una sola tarea que afecta a varios objetos
Un «Active Object» elimina cada comando de la lista después de ejecutarlos, es decir, se puede ejecutar el comando una sola vez. Algunos ejemplos podrían ser:
- Shopping Cart Ejecutando el command
buy()
en cada producto los elimina del carrito. - Financial Transactions Agrupar las transacciones en una sola lista y ejecutarlas con una llamada al «gestor» de eliminaría las transacciones de la cola.
Template Method Pattern
Este patrón permite la reutilización parcial del código. Es practico en algoritmos que solo difieren en una mínima parte.
Cuando Elimina la duplicación de manera sencilla
Por qué no hay duplicación y la flexibilidad no es un problema
En el repositorio de github tenemos un ejemplo
Strategy Pattern
Cuando flexibilidad y reusabilidad son más importantes que simplicidad
Por qué Se utiliza para aplicar grandes trozos, intercambiables de la lógica complicada, mientras se mantiene un algoritmo común.
Por ejemplo, se puede crear una calculadora genérica y luego usar diferentes objetos estrategia para realizar cálculos. Este patrón utilizado con moderación es potente cuando se tienen que definir muchos comportamientos condicionales
Aquí tenemos un ejemplo del patrón.
Facade Pattern
Una fachada es esencialmente una API – una interfaz agradable de cara al cliente. Cuando un cliente llama a uno de estos métodos, la fachada delega en una serie de llamadas que ocultan al cliente toda la complejidad
Cuando Para simplificar una API u ocultar intencionadamente toda la lógica de negocio.
Por qué Así es posible controlar la API, las implementaciones y la lógica de forma independiente.
Aquí tenemos un ejemplo del patrón.
Observer Pattern
Observer Pattern ofrece una manera sencilla de controlar objetos y llevar acabo acciones cuando las condiciones cambian. Existen 2 tipos de implementaciones
- pollling Los Objetos aceptan suscriptores. Los suscriptores observan el objeto y son notificados en eventos específicos.
- Push Al igual que el método polling, los objetos aceptan suscriptores, cuando se produce un evento en el objeto observado se notifica a los suscriptores. Pero además, cuando ocurre una notificación, el observador también recibe un indicio de que el observador puede actuar sobre él.
Cuando Proporcionar un sistema de notificación dentro de la lógica de negocio.
Por qué El modelo ofrece una manera de comunicar eventos a cualquier número de diferentes objetos.
Aquí tenemos un ejemplo del patrón.
Mediator Pattern
El patrón observer puede ampliarse con el patrón mediator. Este modelo toma dos objetos como parámetros. El mediador se suscribe al primer parámetro y cuando ocurre un cambio en el objeto observador, el mediador decide que hacer en el segundo objeto.
Cuando Los objetos afectados no pueden saber nada acerca de los objetos observados.
Por qué Para ofrecer un mecanismo oculto que afecte a otros objetos en el sistema cuando un objeto cambia.
Aquí tenemos un ejemplo del patrón.
Conclusiones
Los patrones de diseño están para resolver problemas a los que nos enfrentamos cada día. Todos sabemos que para ser un buen desarrollador no hay que reinventar la rueda, ya que seguro hay alguien que tiene o ha tenido un problema similar (Stackoverflow es inmenso) y seguro que hay alguna posible solución. Por ello debemos conocer los patrones, el libro Head First Design Pattern esta bien y el repositorio https://github.com/domnikl/DesignPatternsPHP esta genial (y encima en PHP)
2 comentarios en “Uso de patrones de diseño parte 1”