Los diferentes patrones existentes se pueden dividir en:
Permite la creación de objetos sin especificar el tipo concreto.
Se define una interfaz o clase abstracta para crear objetos dependientes relacionados sin especificar la subclase en concreto. Por lo que, la factoría abstracta devuelve una factoría de clases.
En la factoría abstracta, una interfaz es responsable de crear una factoría de objetos relacionados sin especificar sus clases.
✔️ Los beneficios son:
- Aporta poco acoplamiento entre las familias de componentes.
- Aporta mayor consistencia al construir objetos a través de las aplicaciones.
Generalmente utilizado para:
Al crear una factoría, a veces se pueden querer crear un conjunto de objetos relacionados. Se puede solucionar construyendo otra factoría dentro de la factoría.
Se puede utilizar cuando quieres personalizar la lógica inicial de objetos relacionados.
Utilizado para crear objetos complejos paso a paso y devolver el objeto completo.
Utilizado cuando existen muchos constructores sobrecargados.
Una opción es crear un único constructor y llamar a los setters necesarios. Otra usar el patrón Builder.
✔️ Los beneficios son:
- Proporciona aislamiento entre la construcción y la representación del objeto.
- Permite construir el objeto en múltiples fases, teniendo así más control sobre la construcción.
- Proporciona flexibilidad para variar la representación interna de un objeto.
Generalmente utilizado para:
Es una buena elección cuando una clase tiene un método o una factoría estática con demasiados parámetros y sea difícil de manejar.
Permite crear objetos sin especificar la clase.
Define una interfaz para crear un objeto, pero deja a la subclase decidir qué clase inicializar.
✔️ Los beneficios son:
- Proporciona bajo acoplamiento entre componentes os clases que se relacionan haciendo use de interfaces.
Generalmente utilizado para:
Normalmente cuando se necesitan muchos constructores con los mismos parámetros pero diferente comportamiento.
Cuando quieres reutilizar funcionalidad común con diferentes componentes.
Se utiliza cuando:
- Un objeto no sabe exactamente qué subclases tiene que crear.
Crea nuevos objetos de otros existentes usando clone.
✔️ Los beneficios son:
- Reduce el tiempo de crear los objetos.
- Añade y elimina objetos en tiempo de ejecución.
- Simplifica y puede optimizar los casos de uso donde múltiples objetos del mismo tipo tengan los mismos datos.
Generalmente utilizado para:
Para eliminar código repetitivo cuando la configuración requerida para un objeto es pesada.
Asegura que se crea únicamente una instance de un objeto.
Existen varias maneras de implementar Singleton:
- Método tradicional.
- Método sincronizado.
- Variable estática final.
- Mediante clase inner.
- Mediante enum.
✔️ Los beneficios son:
- Proporciona control de acceso a objetos que pueden ser pesados (como conexiones a BDs).
- Ahorra heaps de memoria.
- Es un diseño muy eficiente para entornos multihilo.
Generalmente utilizado para:
Casos en los que quieras inicar un objeto una única vez. Normalmente para las creaciones de BDs.
Permite que dos clases incompatibles trabajen bajo una misma interfaz, convirtiendo la interfaz de una clase en otra interfaz que otra clase espera.
✔️ Los beneficios son:
- Permite comunicar e interactuar con dos o más objetos incompatibles.
- Permite la reusabilidad de antiguas funcionalidades existentes en el sistema.
Generalmente utilizado para:
Cuando se trabaja con interfaces distintas pero con similar comportamiento.
Desacopla una abstracción de su implementación, por lo que dos clases pueden ser independientes.
✔️ Los beneficios son:
- Permite separar la implementación de la abstracción.
- Proporciona la flexibilidad necesaria para cambiar entre dos tipos de clases sin efectos secundarios.
- Permite esconder los detalles de implementación utilizando abstracción entre ellos.
Generalmente utilizado para:
Cuando se quiere cambiar el siguiente comportamiento:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
Refactorizarlo a:
A N
/ \ / \
Aa(N) Ab(N) 1 2
Permite crear estructuras complejas compuestas de otras más pequeñas creando un único objeto de un grupo de objetos.
✔️ Los beneficios son:
- Define jerarquías de clases que contienen objetos primitivos y complejos.
- Hace fácil añadir nuevos tipos de componentes.
- Proporciona flexibilidad.
Generalmente utilizado para:
Útil cuando se necesita tratar selectivamente un grupo de objetos que forman parte de una jerarquía como "iguales" cuando en realidad son distintos.
Añade funcionalidad dinámicamente a un objeto.
✔️ Los beneficios son:
- Permite extender la funcionalidad dinámicamente y estáticamente sin alterar la estructura del objeto existente.
- Se puede añadir una nueva responsabilidad a un objeto dinámicamente.
- Utiliza la composición para la relación de objetos para mantener los principios SOLID.
- Simplifica la codificación al escribir nuevas clases para cada nueva funcionalidad específica en vez de cambiar el código existente.
Generalmente utilizado para:
Extender las funcionalidades de un objeto existente.
Añade una interfaz para controlar las restricciones de acceso a un objeto.
✔️ Los beneficios son:
- Se oculta el objeto del resto del mundo.
- Puede mejorar el rendimiento porque se crean objetos bajo demanda.
Generalmente utilizado para:
Cuando se tienen acciones con un coste de ejecución elevado y no se quieren realizar las acciones asociadas al objeto.
Por ejemplo, un programa que muestra al usuario las fotos que existen en un directorio. Con el patrón proxy se crea el objeto Imagen
pero no se cargan de memoria, sino que se carga únicamente la deseada.
// Suponiendo que un usuario selecciona 3 imágenes de una carpeta, se crean
// 3 objetos Image
Image highResolutionImage1 = new ImageProxy("sample/veryHighResPhoto1.jpeg");
Image highResolutionImage2 = new ImageProxy("sample/veryHighResPhoto2.jpeg");
Image highResolutionImage3 = new ImageProxy("sample/veryHighResPhoto3.jpeg");
// Para cargar una única imagen en memoria se carga el objeto a través
// de un proxy y se llama al método del objeto que se desea.
// De esta manera se ha cargado únicamente una única imagen en memoria
// en lugar de las 3.
highResolutionImage1.showImage();
Proporciona una interfaz unificada a un conjunto de interfaces en un subsistema. Facade define una interfaz de alto nivel que hace el subsistema más fácil de utilizar.
Facade no es más que una interfaz de interfaces para simplificar las interacciones entre el cliente y las demás clases.
Facade oculta la complejidad del sistema y proporciona una interfaz al cliente desde la que el propio cliente puede acceder al sistema.
✔️ Los beneficios son:
- Reduce la complejidad del cliente para interaccionar con los subsistemas.
- Agrupa todos los servicios en interfaces para hacerlos más entendibles.
Generalmente utilizado para:
Cuando una clase contiene múltiples subclases. Normalmente, cuando se realizan múltiples llamadas a distintos métodos con una misma funcionalidad, se pueden guardar todos ellos tras una "fachada". Por ejemplo, si para una acción como "encender coche" y "apagar coche" requiere hacer:
// Encender
deposito.comprobarFuel();
bateria.comprobarBatería();
coche.comprobarContacto();
motor.on();
aireAcondicionado.comprobarEstado();
// Apagar
motor.off();
aireAcondicionado.off();
sistemaElectrico.off();
Se puede simplificar con un objeto Facade
que sea:
facade.encender();
facade.apagar();
Proporciona una forma de acceder a un conjunto de elementos secuencialmente sin exponer la capa de representación.
✔️ Los beneficios son:
- Facilidad para acceder a los objetos de una colección.
- Proporciona una interfaz uniforme a través de diferentes estructuras en una colección.
Generalmente utilizado para:
Cuando la colección de objetos tiene una estructura de datos compleja pero se quiere ocultar esta complejidad al cliente.
Define una dependencia uno-a-muchas entre objetos por lo que cuando un objeto cambia su estado, todos sus dependientes son notificados y actualizados automáticamente.
✔️ Los beneficios son:
- Proporciona relación desacoplada entre el objeto y el observador.
- Proporciona apoyo para el broadcasting.
Generalmente utilizado para:
Cuando tienes una clase que es monitorizada por otra clase. Es decir, cuando hay cambios en el estado de un objeto que se quieran propagar al resto del programa (por ejemplo, notificaciones).
Cuando múltiples entidades están interesadas en alguna posible actualización de otra entidad en particular.
Define el esqueleto de un algoritmo en la superclase pero permite que las subclases sobrescriban pasos del algoritmo sin modificar su estructura.
✔️ Los beneficios son:
- Reduce código repetitivo al reutilizar código.
- El algoritmo crea una plantilla para reutilizar diferentes algoritmos similares.
Generalmente utilizado para:
Cuando quieres permitir al cliente que extienda únicamente pasos particulares de un algoritmo, pero no todo el algoritmo completo.
Cuando se tienen muchas clases que contengan algoritmos casi idénticos pero con diferencias mínimas.
Permite separar algoritmos de los objetos sobre los que operan. Describe una forma de añadir nuevas operaciones a las estructuras de los objetos existentes sin modificar dichas estructuras.
✔️ Los beneficios son:
- Separar la estructura de datos de las operaciones.
Generalmente utilizado para:
Cuando se necesita realizar una operación sobre todos los elementos de una compleja estructura de objetos (como por ejemplo un árbol).