Tengo una aplicación que toma un número entero como entrada y se basa en las llamadas de entrada métodos estáticos de diferentes clases. Cada vez que se agrega un nuevo número, necesitamos agregar otro caso y llamar a un método estático diferente de una clase diferente. Ahora hay 50 casos en el conmutador y cada vez que necesito agregar otro caso, me estremezco. Hay una mejor manera de hacer esto.
Pensé un poco y se me ocurrió esta idea. Yo uso el patrón de estrategia. En lugar de tener un caso de cambio, tengo un mapa de objetos de estrategia con la clave como el entero de entrada. Una vez que se invoca el método, buscará el objeto y llamará al método genérico para el objeto. De esta manera puedo evitar usar la construcción de la caja del interruptor.
¿Qué piensas?
fuente
switch
caso y llamar a un método preexistente en su sistema complejo, o tiene que inventar tanto el método como su llamada?Respuestas:
Amo el polimorfismo. Me encanta SOLID. Me encanta la programación orientada a objetos puros. Odio ver que estos tengan una mala reputación porque se aplican dogmáticamente.
No ha presentado un buen caso para refactorizar la estrategia. La refactorización tiene un nombre por cierto. Se llama Reemplazar condicional con polimorfismo .
He encontrado algunos consejos pertinentes para usted de c2.com :
Tiene un interruptor con 50 cajas y su alternativa es producir 50 objetos. Ah y 50 líneas de código de construcción de objetos. Esto no es progreso. Por qué no? Debido a que esta refactorización no hace nada para reducir el número de 50. Usted usa esta refactorización cuando encuentra que necesita crear otra instrucción de cambio en la misma entrada en otro lugar. Ahí es cuando esta refactorización ayuda porque convierte 100 nuevamente en 50.
Mientras te refieras a "el interruptor" como si fuera el único que tienes, no lo recomiendo. La única ventaja de refactorizar ahora es que reduce las posibilidades de que algún bobo copie y pegue su interruptor de 50 cajas.
Lo que sí recomiendo es mirar de cerca estos 50 casos en busca de puntos en común que se puedan tener en cuenta. Me refiero a 50? De Verdad? ¿Seguro que necesitas tantos casos? Puede que estés tratando de hacer mucho aquí.
fuente
Un mapa de objetos de estrategia solo, que se inicializa en alguna función de su código, donde tiene varias líneas de código parecidas
requiere que usted y sus colegas implementen las funciones / estrategias que se llamarán en clases separadas, de una manera más uniforme (ya que sus objetos de estrategia tendrán que implementar la misma interfaz). Tal código es a menudo un poco más completo que
Sin embargo, aún no lo liberará de la carga de editar este archivo de código cada vez que necesite agregar un nuevo número. Los beneficios reales de este enfoque son diferentes:
la inicialización del mapa ahora se separa del código de despacho que realmente llama a la función asociada a un número específico, y este último ya no contiene esas 50 repeticiones, simplemente se verá
myMap[number].DoIt(someParameters)
. Por lo tanto, no es necesario tocar este código de envío cada vez que llega un nuevo número y se puede implementar de acuerdo con el principio de Abierto-Cerrado. Además, cuando obtiene requisitos en los que necesita extender el código de envío en sí, ya no tendrá que cambiar 50 lugares, sino solo uno.el contenido del mapa se determina en tiempo de ejecución (mientras que el contenido de la construcción del conmutador se determina antes del tiempo de compilación), por lo que le brinda la oportunidad de hacer que la lógica de inicialización sea más flexible o ampliable.
Entonces, sí, hay algunos beneficios, y este es seguramente un paso hacia un código más SÓLIDO. Sin embargo, si vale la pena refactorizar, es algo que usted o su equipo tendrán que decidir por sí mismos. Si no espera que se cambie el código de envío, se cambie la lógica de inicialización y la legibilidad del mismo
switch
no sea un problema real, entonces su refactorización podría no ser tan importante ahora.fuente
Doit1
,Doit2
, etc, con unDoit
método que tiene muchas implementaciones diferentes.doTheThing()
un método del símbolo de entrada. Entonces no necesitas mantener el mapa.Estoy totalmente a favor de la estrategia descrita en la respuesta de @DocBrown .
Voy a sugerir una mejora a la respuesta.
Las llamadas
Se puede distribuir. No tiene que volver al mismo archivo para agregar otra estrategia, que se adhiere al principio de Open-Closed aún mejor.
Digamos que implementa
Strategy1
en el archivo Strategy1.cpp. Puede tener el siguiente bloque de código en él.Puede repetir el mismo código en cada archivo StategyN.cpp. Como puede ver, será mucho código repetido. Para reducir la duplicación de código, puede usar una plantilla que se puede colocar en un archivo que sea accesible para todas las
Strategy
clases.Después de eso, lo único que debe usar en Strategy1.cpp es:
La línea correspondiente en StrategyN.cpp es:
Puede llevar el uso de plantillas a otro nivel utilizando una plantilla de clase para las clases de estrategia concretas.
Y luego, en lugar de
Strategy1
usarConcreteStrategy<1>
.Cambie la clase auxiliar para registrar
Strategy
s en:Cambie el código en Strateg1.cpp a:
Cambie el código en StrategN.cpp a:
fuente