design patterns - lista - Patrones de diseño para evitar
patrones estructurales (12)
Mucha gente parece estar de acuerdo, que el patrón Singleton tiene una serie de inconvenientes y algunos incluso sugieren evitar el patrón por completo. Hay una excelente discusión aquí . Dirija cualquier comentario sobre el patrón de Singleton a esa pregunta.
Mi pregunta : ¿Hay otros patrones de diseño que deberían evitarse o utilizarse con sumo cuidado?
Los patrones son complejos
Todos los patrones de diseño deben usarse con cuidado. En mi opinión , debería refactorizarse hacia patrones cuando existe una razón válida para hacerlo en lugar de implementar un patrón de inmediato. El problema general con el uso de patrones es que agregan complejidad. El uso excesivo de patrones hace que una determinada aplicación o sistema sea engorroso para desarrollar y mantener.
La mayoría de las veces, hay una solución simple y no necesitará aplicar ningún patrón específico. Una buena regla empírica es usar un patrón cada vez que las piezas de código tienden a ser reemplazadas o necesitan cambiarse con frecuencia y estar preparados para asumir la advertencia del código complejo cuando se utiliza un patrón.
Recuerde que su objetivo debe ser la simplicidad y emplear un patrón si ve una necesidad práctica para apoyar el cambio en su código.
Principios sobre patrones
Puede parecer un debate para usar patrones si pueden conducir evidentemente a soluciones complejas y sobrediseñadas. Sin embargo, es mucho más interesante para un programador leer técnicas y principios de diseño que sientan las bases para la mayoría de los patrones. De hecho, uno de mis libros favoritos sobre "patrones de diseño" subraya esto al reiterar los principios aplicables al patrón en cuestión. Son lo suficientemente simples como para ser útiles que los patrones en términos de relevancia. Algunos de los principios son lo suficientemente generales como para abarcar más que la programación orientada a objetos (OOP), como el Principio de sustitución de Liskov , siempre que pueda construir módulos de su código.
Hay una multitud de principios de diseño, pero los descritos en el primer capítulo del libro de GoF son bastante útiles para empezar.
- Programe una ''interfaz'', no una ''implementación''. (Gang of Four 1995: 18)
- Favorecer la "composición de objetos" sobre la "herencia de clases". (Gang of Four 1995: 20)
Permita que esos se sumerjan en usted por un tiempo. Cabe señalar que cuando se escribió GoF, una interface significa cualquier cosa que sea una abstracción (lo que también significa superclases), que no debe confundirse con la interfaz como un tipo en Java o C #. El segundo principio proviene del uso excesivo observado de la herencia, que lamentablemente todavía es común en la actualidad .
Desde allí, puede leer los principios SOLID, que fue dado a conocer por Robert Cecil Martin (también conocido como Tío Bob) . Scott Hanselman entrevistó a tío Bob en un podcast sobre estos principios :
- S ingle Principio de responsabilidad
- O pluma cerrado principio
- L iskov Principio de Sustitución
- I nterface Principio de segregación
- D ependencia Principio de inversión
Estos principios son un buen comienzo para leer y discutir con sus compañeros. Puede encontrar que los principios se entrelazan entre sí y con otros procesos como la separación de preocupaciones y la inyección de dependencia . Después de hacer TDD por un tiempo, también puede encontrar que estos principios se dan naturalmente en la práctica, ya que necesita seguirlos hasta cierto punto para crear pruebas unitarias aisladas y repetibles .
Algunos dicen que el localizador de servicios es un anti patrón.
Creo que Active Record es un patrón usado en exceso que fomenta la mezcla de la lógica empresarial con el código de persistencia. No hace un buen trabajo al ocultar la implementación de almacenamiento de la capa de modelo y vincula los modelos a una base de datos. Hay muchas alternativas (descritas en PoEAA), como Table Data Gateway, Row Data Gateway y Data Mapper, que a menudo proporcionan una mejor solución y, desde luego, ayudan a proporcionar una mejor abstracción del almacenamiento. Además, su modelo no debería ser almacenado en una base de datos; ¿qué hay de almacenarlos como XML o acceder a ellos utilizando servicios web? ¿Qué tan fácil sería cambiar el mecanismo de almacenamiento de sus modelos?
Dicho esto, Active Record no siempre es malo y es perfecto para aplicaciones más simples donde las otras opciones serían exageradas.
Creo que el patrón de Método de plantilla generalmente es un patrón muy peligroso.
- Muchas veces utiliza su jerarquía de herencia por "las razones incorrectas".
- Las clases base tienden a ensuciarse con todo tipo de código no relacionado.
- Te obliga a bloquear el diseño, a menudo bastante temprano en el proceso de desarrollo. (Bloqueo prematuro en muchos casos)
- Cambiar esto en una etapa posterior se vuelve cada vez más difícil.
Creo que el patrón del observador tiene mucho por qué responder, funciona en casos muy generales, pero a medida que los sistemas se vuelven más complejos se convierte en una pesadilla, necesita notificaciones OnBefore (), OnAfter () y, a menudo, tareas asincrónicas para evitar entrada Una solución mucho mejor es desarrollar un sistema de análisis de dependencia automático que instrumenta todos los accesos a objetos (con barreras de lectura) durante los cálculos y crea automáticamente una ventaja en un gráfico de dependencia.
El iterador es un patrón más de GoF para evitar, o al menos usarlo solo cuando no hay ninguna alternativa disponible.
Las alternativas son:
para cada bucle Esta construcción está presente en la mayoría de los lenguajes principales y puede usarse para evitar iteradores en la mayoría de los casos.
selectores à la LINQ o jQuery. Deben usarse cuando for-each no es apropiado porque no se deben procesar todos los objetos del contenedor. A diferencia de los iteradores, los selectores permiten manifestar en un solo lugar qué objetos amables se procesarán.
Es simple ... evite patrones de diseño que no le resulten claros o aquellos en los que no se sienta cómodo .
Para nombrar algunos ...
hay algunos patrones poco prácticos , como por ejemplo:
-
Interpreter
-
Flyweight
también hay algunos más difíciles de comprender , como por ejemplo:
-
Abstract Factory
- El patrón completo de fábrica abstracto con familias de objetos creados no es tan fácil como parece ser -
Bridge
: puede ser demasiado abstracto, si la abstracción y la implementación se dividen en subárboles, pero en algunos casos es un patrón muy útil -
Visitor
: la comprensión del mecanismo de despacho doble es realmente DEBE
y hay algunos patrones que parecen terriblemente simples , pero que no son tan claros debido a varias razones relacionadas con su principio o implementación:
-
Singleton
: patrón no del todo malo, demasiado usado demasiado (a menudo allí, donde no es adecuado) -
Observer
- gran patrón ... solo hace que el código sea mucho más difícil de leer y depurar -
Prototype
: el compilador comercial comprueba el dinamismo (que puede ser bueno o malo ... depende) -
Chain of responsibility
: demasiado a menudo forzada / artificialmente en el diseño
Para aquellos "poco prácticos", uno debería pensar antes de usarlos, porque en general hay una solución más elegante en alguna parte.
Para los "más difíciles de captar" ... son realmente de gran ayuda, cuando se utilizan en lugares adecuados y cuando se implementan bien ... pero son una pesadilla, cuando se usan de forma incorrecta.
Ahora, ¿qué sigue?
- Los primeros patrones de diseño de Head First son obligatorios
- Sourcemaking es el "primer auxilio"
Espero no ser golpeado demasiado por esto. Christer Ericsson escribió dos artículos ( one , two ) sobre el tema de los patrones de diseño en su blog de detección de colisiones en tiempo real . Su tono es bastante duro, y tal vez un poco provocativo, pero el hombre sabe lo que hace, así que no lo descartaría como desvaríos de un lunático.
La que más preocupaba a los autores de Design Patterns era el patrón "Visitante".
Es un "mal necesario", pero a menudo se usa demasiado y su necesidad a menudo revela un defecto más fundamental en su diseño.
Un nombre alternativo para el patrón "Visitante" es "Envío múltiple", porque el patrón de Visitante es lo que termina cuando desea utilizar un lenguaje OO de despacho de un solo tipo para seleccionar el código a usar según el tipo de dos (o más) diferentes objetos.
El ejemplo clásico es que tiene la intersección entre dos formas, pero hay un caso aún más simple que a menudo se pasa por alto: la comparación de la igualdad de dos objetos heterogéneos.
De todos modos, a menudo terminas con algo como esto:
interface IShape
{
double intersectWith(Triangle t);
double intersectWith(Rectangle r);
double intersectWith(Circle c);
}
El problema con esto es que has unido todas tus implementaciones de "IShape". Ha sugerido que cada vez que desee agregar una nueva forma a la jerarquía, deberá cambiar todas las demás implementaciones de "Forma" también.
A veces, este es el diseño mínimo correcto, pero piénselo bien. ¿Su diseño realmente exige que necesite despachar en dos tipos? ¿Estás dispuesto a escribir cada uno de la explosión combinatoria de métodos múltiples?
A menudo, al introducir otro concepto, puede reducir el número de combinaciones que realmente tendrá que escribir:
interface IShape
{
Area getArea();
}
class Area
{
public double intersectWith(Area otherArea);
...
}
Por supuesto, depende, a veces es necesario escribir un código para manejar todos esos casos diferentes, pero vale la pena tomarse una pausa y pensar antes de tomar la iniciativa y usar Visitor. Puede ahorrarle mucho dolor más adelante.
No creo que deba evitar Design Patterns (DP), y no creo que deba obligarse a utilizar DP cuando planifique su arquitectura. Solo deberíamos utilizar DP cuando surgen de forma natural de nuestra planificación.
Si definimos desde un principio que queremos usar un DP determinado, muchas de nuestras decisiones futuras de diseño serán influenciadas por esa elección, sin garantía de que el DP que elegimos sea adecuado para nuestras necesidades.
Una cosa que tampoco deberíamos hacer es tratar a un DP como una entidad inmutable, debemos adaptar el patrón a nuestras necesidades.
Entonces, sumarizando, no creo que debamos evitar los DP, deberíamos abrazarlos cuando ya estén tomando forma en nuestra arquitectura.
Singletons: una clase que usa singleton X tiene una dependencia que es difícil de ver y difícil de aislar para las pruebas.
Se usan con mucha frecuencia porque son convenientes y fáciles de entender, pero realmente pueden complicar las pruebas.
Un complemento a la publicación de Spoike, Refactoring to Patterns es una buena lectura.