El patrón de estrategia funciona bien para evitar grandes construcciones if ... else y hace que sea más fácil agregar o reemplazar funcionalidades. Sin embargo, todavía deja un defecto en mi opinión. Parece que en cada implementación todavía tiene que haber una construcción ramificada. Puede ser una fábrica o un archivo de datos. Como ejemplo, tome un sistema de pedidos.
Fábrica:
// All of these classes implement OrderStrategy
switch (orderType) {
case NEW_ORDER: return new NewOrder();
case CANCELLATION: return new Cancellation();
case RETURN: return new Return();
}
El código después de esto no necesita preocuparse, y solo hay un lugar para agregar un nuevo tipo de orden ahora, pero esta sección de código aún no es extensible. Sacarlo a un archivo de datos ayuda un poco a la legibilidad (discutible, lo sé):
<strategies>
<order type="NEW_ORDER">com.company.NewOrder</order>
<order type="CANCELLATION">com.company.Cancellation</order>
<order type="RETURN">com.company.Return</order>
</strategies>
Pero esto aún agrega código repetitivo para procesar el archivo de datos: un código más fácil de probar y relativamente estable, pero complejidad adicional no obstante.
Además, este tipo de construcción no prueba bien la integración. Cada estrategia individual puede ser más fácil de probar ahora, pero cada nueva estrategia que agregue es una complejidad adicional para probar. Es menos de lo que tendría si no hubiera usado el patrón, pero todavía está allí.
¿Hay alguna manera de implementar el patrón de estrategia que mitigue esta complejidad? ¿O es tan simple como parece, e intentar ir más allá solo agregaría otra capa de abstracción para poco o ningún beneficio?
fuente
eval
... ¿podría no funcionar en Java pero quizás en otros idiomas?Respuestas:
Por supuesto no. Incluso si usa un contenedor de IoC, tendrá que tener condiciones en algún lugar, decidiendo qué implementación concreta inyectar. Esta es la naturaleza del patrón de estrategia.
Realmente no veo por qué la gente piensa que esto es un problema. Hay declaraciones en algunos libros, como Fowler's Refactoring , que si ve un interruptor / caso o una cadena de si / elses en el medio de otro código, debe considerar que huele y busca moverlo a su propio método. Si el código dentro de cada caso es más que una línea, tal vez dos, entonces debería considerar convertir ese método en un Método de Fábrica, devolviendo Estrategias.
Algunas personas han considerado que esto significa que un interruptor / caso es malo. Este no es el caso. Pero debería residir solo, si es posible.
fuente
Sí , utilizando un hashmap / diccionario donde se registra cada implementación de Estrategia. El método de fábrica se convertirá en algo así como
Cada implementación de estrategia debe registrar una fábrica con su orderType y alguna información sobre cómo crear una clase.
Puede usar el constructor estático para el registro, si su idioma lo admite.
El método de registro no hace más que agregar un nuevo valor al hashmap:
[actualización 2012-05-04]
Esta solución es mucho más compleja que la "solución de cambio" original que preferiría la mayor parte del tiempo.
Sin embargo, en un entorno donde las estrategias cambian a menudo (es decir, el cálculo del precio según el cliente, el tiempo, ...), esta solución de mapa hash combinada con un IoC-Container podría ser una buena solución.
fuente
factory.register(NEW_ORDER, NewOrder.class);
limpiador, o menos una violación de OCP, que las filas decase NEW_ORDER: return new NewOrder();
?La "estrategia" consiste en tener que elegir entre algoritmos alternativos al menos una vez , no menos. En algún lugar de su programa, alguien tiene que tomar una decisión, tal vez el usuario o su programa. Si utiliza un IoC, una reflexión, un evaluador de archivos de datos o una construcción de interruptor / caso para implementar esto, no cambia la situación.
fuente
El patrón de estrategia se usa cuando se especifican posibles comportamientos y se usa mejor cuando se designa un controlador en el inicio. La especificación de qué instancia de la estrategia usar se puede hacer a través de un Mediador, su contenedor de IoC estándar, alguna Fábrica como usted describe, o simplemente usando la correcta según el contexto (ya que a menudo, la estrategia se suministra como parte de un uso más amplio de la clase que lo contiene).
Para este tipo de comportamiento en el que se deben invocar diferentes métodos basados en datos, entonces sugeriría utilizar las construcciones de polimorfismo proporcionadas por el lenguaje en cuestión; No es el patrón de estrategia.
fuente
Al hablar con otros desarrolladores donde trabajo, encontré otra solución interesante, específica para Java, pero estoy seguro de que la idea funciona en otros idiomas. Construya una enumeración (o un mapa, no importa demasiado cuál) usando las referencias de clase e instancia usando la reflexión.
Esto reduce drásticamente el código de fábrica:
(Por favor ignore el mal manejo de errores - código de ejemplo :))
No es el más flexible, ya que aún se requiere una compilación ... pero reduce el cambio de código a una línea. Me gusta que los datos estén separados del código de fábrica. Incluso puede hacer cosas como establecer parámetros, ya sea utilizando clases / interfaces abstractas para proporcionar un método común o creando anotaciones en tiempo de compilación para forzar firmas de constructor específicas.
fuente
La extensibilidad se puede mejorar haciendo que cada clase defina su propio tipo de orden. Luego, su fábrica selecciona la que coincida.
Por ejemplo:
fuente
Creo que debería haber un límite en cuántas ramas son aceptables.
Por ejemplo, si tengo más de ocho casos dentro de mi declaración de cambio, entonces vuelvo a evaluar mi código y busco lo que puedo refactorizar. Muy a menudo encuentro que ciertos casos se pueden agrupar en una fábrica separada. Mi suposición aquí es que hay una fábrica que construye estrategias.
De cualquier manera, no puede evitar esto y en algún lugar tendrá que verificar el estado o el tipo del objeto con el que está trabajando. Luego construirá una estrategia para eso. Buena separación de preocupaciones.
fuente