El tema de los patrones de diseño es bastante popular. Se grabaron muchos videos y se escribieron artículos. Combina todos estos materiales con un "antipatrón". Complejidad accidental. Como resultado, los ejemplos son abstrusos, la descripción es confusa, la forma de aplicar no está clara. Y la tarea principal de los patrones de diseño: la simplificación (del código y el trabajo en general) no se logra. Después de todo, el uso de la plantilla requiere un esfuerzo adicional. Aproximadamente lo mismo que las pruebas unitarias.
Trataré de explicar los patrones de diseño en términos de cómo aplicarlos, dónde y por qué.
Seis generadores pueden atribuirse a:
- Prototipo
- Fábrica abstracta,
- Método de la fábrica
- Constructor
- Singleton
- Inicialización perezosa.
Todos los demás patrones que se relacionan con los generadores son un caso especial de aplicación y no tiene sentido detenerse en ellos.
Los patrones de generación se pueden dividir en tres grupos, en función de la pregunta a la que responden. Entonces, tres preguntas:
Donde
Tres patrones responden a esta pregunta: Prototipo, Fábrica abstracta y Método de fábrica.
Un poco sobre los términosDentro del marco del concepto de OOP, solo hay tres lugares donde teóricamente es posible generar una nueva instancia.
- El producto es la clase que se instancia.
- El cliente es la clase que usará la instancia instanciada.
- Socio: cualquier tercera clase en el campo de visibilidad del Cliente.
En realidad, estos patrones determinan el lugar de la generación. Además, están conectados jerárquicamente y tienen un alcance diferente. La conexión se muestra en la figura, las flechas determinan la dirección de las llamadas.

En su implementación, el "Método de Fábrica" puede delegar la generación de una instancia a la "Fábrica" existente o al "Prototipo". Sin embargo, el "prototipo" no debería depender de nadie y hacer todo por sí mismo. Ahora con más detalle.
"Prototipo"
Esta plantilla corresponde al lugar "Producto", de hecho, es el constructor de la clase. Por consiguiente, siempre se genera una instancia de una clase específica (previamente conocida).
Dentro del marco de esta plantilla, el constructor conoce solo los parámetros que se le pasan directamente (el número de parámetros tiende al número de campos de clase). Por supuesto, hay acceso completo a todos los campos y propiedades de la clase creada.
Los métodos implementados correctamente del "Prototipo" le permiten deshacerse de los métodos de inicialización adicionales en público. A su vez, la interfaz externa de la clase se vuelve más fácil y menos tentadora de usar la clase para otros fines.
Lo que nos da esta plantilla:
- Baja conectividad: la clase solo se conoce a sí misma, no depende de datos externos;
- Extensibilidad: los constructores se pueden redefinir o agregar a los descendientes;
De las desventajas:
- Para clases complejas, es posible que deba pasar muchos parámetros para la inicialización. Aunque hay una solución trivial.
- El uso directo en el Cliente puede degradar la legibilidad y prácticamente cierra la posibilidad de redefinir el tipo de instancia que se genera en el descendiente del Cliente.
La plantilla más popular. Todos lo usan, pero pocos saben lo que usa. Es bueno hasta que se obtenga el primer modelo de trabajo, hasta que las clases y sus relaciones estén completamente definidas. Después de eso, el procesamiento y la abstracción creciente son obligatorios.
"Fábrica abstracta"
Un compañero de clase. Puede ser especializado, o "combinar". Puede ser estático (sin instancia). Un ejemplo de "combinación" puede ser una clase de configuración. También puede estar oculto detrás de la Fachada.
"Fábrica" generalmente ve todas las configuraciones globales de la aplicación (o un subsistema separado). La generación inmediata puede delegarse en el prototipo. Al mismo tiempo, el número de parámetros de entrada en el método Factory será menor que en un constructor Prototype similar. La fábrica no decide a quién crear según los parámetros entrantes.
Esta plantilla es muy conveniente y fácil de implementar, pero requiere un diseño preliminar. Si crea fábricas para todo, esto complicará el código. De hecho, obtenemos un análogo del prototipo, pero pasamos a una clase de terceros.
De los profesionales:
- Buena redefinición en descendientes
- Llamada simplificada
- Sobre la base de la Fábrica, es fácil implementar una sustitución (plantilla de Estado)
Pero también hay desventajas:
- Requiere diseño, especialmente para fábricas universales (que se utilizan en muchos proyectos). En otras palabras, conseguir una buena fábrica de inmediato no es fácil.
- Es muy fácil estropear el código, hay dos áreas principales:
- Deslizándose hacia el prototipo, pero en una clase externa. Los métodos están sobrecargados con parámetros; hay muchos métodos en sí mismos. Como resultado, la herencia es difícil, tanto en la Fábrica como en el Cliente.
- Fábrica con método universal. Este método devuelve cualquier instancia dependiendo de los parámetros pasados. El resultado, como en el primer caso.
Muy popular Esta plantilla es utilizada por quienes asistieron al curso GoF. Como regla, el código se vuelve aún peor que "antes de aplicar las plantillas".
Tiene sentido cuando las fábricas aparecen durante la primera revisión del código. En esta etapa, ya se conocen combinaciones de parámetros para las instancias creadas, y no será difícil escribir métodos Factory generalizados. Como resultado, las llamadas en el Cliente se simplificarán.
En algunos casos, es conveniente ocultar fábricas detrás de la fachada. Por ejemplo, la aplicación tiene una docena de sus fábricas y una docena de bibliotecas. Para ellos, puedes construir una fachada. Esto permitirá no vincular bibliotecas a cada módulo, y también es fácil reemplazar una fábrica por otra.
Método de la fábrica
La cima de la abstracción en los patrones generativos. Lugar de origen Cliente. La clase en la que cada producto se coloca en el Método de Fábrica tiene todas las posibilidades de una larga vida. Si no hay fanatismo, entonces el eje de desarrollo asumido necesariamente debe basarse en esta plantilla.
El método de fábrica no ve más allá de su clase. El número de parámetros transmitidos directamente debe ser mínimo (en el límite de cero). El método en sí debe construirse teniendo en cuenta la posibilidad de superposición en el descendiente.
Un error común es la inicialización complicada en un método. Por ejemplo, al crear una instancia compleja (la plantilla Builder), la creación de todas las partes del objeto futuro se coloca en un método. Como resultado, dicho método es difícil de superponer en el descendiente.
De los profesionales:
- Será fácil hacer coincidir el método de plantilla de plantilla
- Obtenemos un código conciso en el que la lógica es claramente visible (no es necesario mirarla entre un montón de métodos y parámetros)
Básicamente no hay contras.
Esta plantilla casi nunca se usa. Como regla general, solo se puede ver en proyectos con una profunda elaboración preliminar. Ideal cuando el Método de Fábrica delega la generación a la "Fábrica" o "Prototipo".
Pequeño ejemplo
Tenemos una clase para iniciar sesión en un archivo en el disco duro. Así es como podrían verse los métodos genéricos dentro de los patrones "¿Dónde?":
Prototipo:
constructor Create(aFilename: string; aLogLevel: TLogLevel);
Todo lo que el diseñador debe saber se le pasa en forma de parámetros.
Fábrica:
function GetLogger(aLogLevel: TLogLevel): ILogger;
La fábrica sabe en qué archivo escribir, como se especifica en la configuración de la aplicación.
Método de fábrica
function NewLogger: ILogger;
En la clase Cliente, se sabe con qué detalle registrar.
En este diseño, para reemplazar la clase de registro con un código auxiliar, es suficiente redefinir NewLogger en el descendiente del cliente. Esto es útil cuando se realizan pruebas unitarias.
Para iniciar sesión en la base de datos, es suficiente anular el método GetLogger en el descendiente de Factory.