Los patrones de diseño son plantillas para solucionar problemas de diseño recurrentes que podrás encontrar en cualquier aplicación o juego. Estas plantillas aportan una forma estandarizada de solucionar los problemas que ya está contrastada y validada.
La aplicación de estos patrones aportan un lenguaje común con el que comunicarse entre programadores, esto es debido a que cada patrón intenta resolver un problema concreto y tiene asignado un nombre único. Cuando un programador experimentado escucha el nombre de un patrón ya sabe que problemática intenta resolver y cuál es su implementación.
Los patrones también aportarán mantenibilidad y flexibilidad al proyecto en el que está trabajando, esto es así porque los patrones son soluciones elegantes a problemas concretos y lo intentan resolver de la mejor forma posible. Además suelen respetar los principios SOLID.
Una vez que conozcas y hayas interiorizado los patrones de diseño serás capaz de migrarlos a cualquier motor, sistema o lenguaje. En cuanto se te presente un problema podrás compararlo con los problemas tipo que intenta resolver cada patrón y aplicar este patrón para resolverlo de una forma elegante.
Para aplicar estos patrones no necesitas ser un genio de la programación, solo necesitas conocerlos y experimentar con ellos para absorber todo el conocimiento expuesto en este libro.
Patrones de diseño de creación
Como su nombre indica, los patrones de creación abstraen el proceso de creación de los objetos, lo que nos ayuda a separar como se comporta un objeto (o instancia) de cómo lo tenemos que construir.
Dicho de otra forma, estos patrones esconden los detalles de cómo vamos a crear cierto objeto, si lo haremos con un new, o utilizaremos un Instantiate() en Unity, o si una vez construido necesitamos llamar a varios métodos para pasarle sus colaboradores, etc.
Con los patrones Builder y Factory method podremos crear nuestros objetos de la forma más cómoda y desacoplada posible, lo que nos permitirá extender nuestro sistema muy fácilmente.
Singleton y Monostate los utilizamos cuando solo queremos que exista un único objeto, o una única representación de estos datos. Se suele decir que el Singleton es un mal patrón, y para mí es una verdad a medias. Cuando se abusa de este patrón tenemos un problema, pero en momentos dados nos puede aportar mucha velocidad de desarrollo.
Patrones de diseño estructurales
Los patrones estructurales se encargan de resolver cómo se combinan las clases, ya sea mediante herencia o composición, para crear estructuras más complejas.
El patrón Adapter nos ayudará a prevenir los cambios sobre una librería, o plugin, externa, algo que sabemos que puede cambiar y cuando esto pase no queremos que afecte a todo nuestro código.
El patrón Facade estoy seguro de que ya lo estas utilizando aunque no lo llames así. Si tienes una clase que esconde a otras N clases de forma que el consumidor no sabe que hay más cosas detrás, esto es un patrón Fachada.
El Decorator también se suele utilizar mucho sin saberlo. Básicamente encapsula una clase, ya sea por herencia o composición) y extiende su comportamiento. No confundáis esto con un Adapter, se parecen pero un Adapter no extiende la funcionalidad.
Patrones de diseño de comportamiento
Los patrones de comportamiento hablan de la lógica de nuestro sistema, algoritmos, responsabilidades de las clases y cómo se comunican entre sí.
Uno de mis patrones de comportamiento preferido es el patrón Strategy, hoy en día también conocido como inyección de dependencias. Básicamente te inyectas por constructor, o como sea, una interfaz del comportamiento que quieres utilizar y en cualquier momento puedes cambiar como se comporta esa clase solo cambiando el objeto que pasas en el constructor, sin modificar nada de la clase.
El patrón Mediator también me gusta bastante, sobretodo lo utilizo para la vista, este patrón nos proporciona una clase con la que hablar y será esta la que medie con subclases que dependen de esta.
Tabla de referencia rápida
Patrones de creación
Singleton
Se asegura de que solo exista una única instancia y proporciona un acceso global a esta.
Factory method
El propósito de este patrón es abstraer cómo creamos objetos de un tipo concreto sin que nos tengamos que preocupar de los detalles, simplemente le tenemos que proporcionar la información necesaria para que decida qué objeto debe crear.
Builder
Nos permite crear un objeto por partes, le vamos proporcionando las distintas partes y cuando ya lo tiene todo le pedimos que nos entregue el objeto construido.
Abstract Factory
Nos proporciona la funcionalidad de poder crear una familia de objetos relacionados, o dependientes entre sí.
Patrones estructurales
Adapter
La principal función de este patrón es «adaptarse» a una interfaz, o contrato, de forma que los consumidores no se enteren de que hay detrás.
También nos puede ayudar para convertir clases estáticas en clases que tengamos que instanciar, y por lo tanto testeables e inyectables.
Decorator
Este patrón de diseño de software nos proporciona una forma fácil de añadir responsabilidades adicionales a un objeto de forma dinámica y sin tener que modificar este objeto. Además proporciona una alternativa a la herencia para extender su funcionalidad.
Facade
Hace de de intermediario (de fachada) entre los consumidores y distintos sistemas. En lugar de que el consumidor tenga que hablar con 2 o 3 sistemas para hacer una acción, habla con la fachada y esta ya se encargará de hablar con los sistemas.
Composite
Su objetivo es que el consumidor no sepa si está hablando con un solo objeto o si detrás hay mil objetos de ese tipo. Además de hacerlo respetando Open-Close.
Patrones de comportamiento
State
El objetivo de este patrón es permitirnos de forma sencilla que un objeto cambie su comportamiento en función del estado en el que se encuentra.
Command
En resumidas cuentas este patrón es simplemente una orden, cierta lógica que queremos hacer, encapsulada en una clase y con un método Execute.
Mediator
El objetivo de este patrón es encapsular como interactúan varios objetos entre sí, lo que nos ayuda a tener un bajo acoplamiento.
Template Method
El cometido principal de este patrón es definir un esqueleto común para un algoritmo y dejar los detalles de implementación para los hijos.
Observer
El objetivo principal de este patrón es avisarnos cuándo se realiza algún cambio sobre el objeto que estamos observando.
Strategy
Nos permite definir una familia de algoritmos, o clases, de forma que sean intercambiables entre sí.
Otros patrones
Service Locator
El objetivo del Service locator es proporcionar un punto global desde el que se pueda obtener un servicio sin acoplarse a la implementación concreta, esto quiere decir que utiliza la interfaz y por lo tanto lo podremos testear.
Null Object y Optional
El NullObject actúa como un objeto «normal» pero su comportamiento está vacío, esto nos ayuda a no tener que trabajar con referencias nulas.
Object Pool
El objetivo del Object Pool es optimizar el uso de la memoria y reducir el coste de instanciar y destruir objetos, en lugar de esto los vamos a reciclar.
Libros recomendados sobre patrones
- El libro de GANG OF FOUR, patrones de diseño (Erich Gamma)
- Game Programming Patterns (Robert Nystrom – Electronic Arts)
- Agile Principles, Patterns, and Practices in C# (Robert C. Martin)
- ¿Cómo empezar en el desarrollo de videojuegos?
- Patrones de diseño – Template Method
- Patrones de diseño – Service Locator
- Cómo aumentar el rendimiento de tu equipo en Unity
- Patrones de diseño – Composite
- Devlog #00 – Empezamos proyecto nuevo