Tengo esta antigua implementación del patrón de comando. Es como pasar un contexto a través de toda la implementación de DIOperation , pero más tarde me di cuenta, en el proceso de aprendizaje y aprendizaje (que nunca se detiene), que no es óptimo. También creo que la "visita" aquí realmente no encaja y simplemente confunde.
Realmente estoy pensando en refactorizar mi código, también porque un Comando no debe saber nada acerca de los demás y en este momento todos comparten los mismos pares clave-valor. Es realmente difícil mantener qué clase posee qué valor clave, lo que a veces conduce a variables duplicadas.
Un ejemplo de caso de uso: digamos que CommandB requiere UserName, que se establece mediante CommandA . ¿Debería CommandA establecer la clave UserNameForCommandB = John ? ¿O deberían compartir un nombre de usuario común = valor-clave de John ? ¿Qué pasa si el nombre de usuario es utilizado por un tercer comando?
¿Cómo puedo mejorar este diseño? ¡Gracias!
class DIParameters {
public:
/**
* Parameter setter.
*/
virtual void setParameter(std::string key, std::string value) = 0;
/**
* Parameter getter.
*/
virtual std::string getParameter(std::string key) const = 0;
virtual ~DIParameters() = 0;
};
class DIOperation {
public:
/**
* Visit before performing execution.
*/
virtual void visitBefore(DIParameters& visitee) = 0;
/**
* Perform.
*/
virtual int perform() = 0;
/**
* Visit after performing execution.
*/
virtual void visitAfter(DIParameters& visitee) = 0;
virtual ~DIOperation() = 0;
};
fuente
Respuestas:
Estoy un poco preocupado por la mutabilidad de sus parámetros de comando. ¿Es realmente necesario crear un comando con parámetros que cambian constantemente?
Problemas con su enfoque:
¿Desea que otros hilos / comandos cambien sus parámetros mientras
perform
continúa?¿Quieres
visitBefore
yvisitAfter
del mismoCommand
objeto ser llamado con diferentesDIParameter
objetos?¿Desea que alguien alimente los parámetros de sus comandos, sobre los cuales los comandos no tienen idea?
Nada de esto está prohibido por su diseño actual. Si bien un concepto de parámetro clave-valor genérico tiene sus ventajas a veces, no me gusta con respecto a una clase de comando genérico.
Ejemplo de consecuencias:
Considere una realización concreta de su
Command
clase, algo asíCreateUserCommand
. Ahora, obviamente, cuando solicita que se cree un nuevo usuario, el comando necesitará un nombre para ese usuario. Dado que yo sé losCreateUserCommand
y lasDIParameters
clases, qué parámetro he de poner?Podría establecer el
userName
parámetro, ousername
... ¿trata los parámetros sin distinción entre mayúsculas y minúsculas? Realmente no lo sabría ... oh, espera ... tal vez es soloname
?Como puede ver, la libertad que obtiene de un mapeo genérico de valores clave implica que usar sus clases como alguien que no las implementó es injustificadamente difícil. Al menos necesitaría proporcionar algunas constantes para sus comandos para que otros sepan qué teclas son compatibles con ese comando.
Posibles enfoques de diferentes diseños:
Parameter
instancias en inmutables, puede reutilizarlas libremente entre diferentes comandos.UserParameter
clase que contiene exactamente los parámetros que necesitaría para los comandos que involucran a un usuario, sería mucho más simple trabajar con esta API. Todavía podría tener herencia en los parámetros, pero ya no tendría sentido que las clases de comando tomen parámetros arbitrarios; por supuesto, esto significa que los usuarios de API saben qué parámetros se requieren exactamente.visitBefore
yvisitAfter
, al mismo tiempo que los reutiliza con diferentes parámetros, estará abierto al problema de ser llamado con diferentes parámetros. Si los parámetros deben ser los mismos en llamadas a varios métodos, debe encapsularlos en el comando de modo que no puedan cambiarse por otros parámetros entre las llamadas.fuente
Lo bueno de los principios de diseño es que tarde o temprano entran en conflicto entre sí.
En la situación descrita, creo que preferiría ir con algún tipo de contexto en el que cada comando pueda tomar información y poner información (especialmente si son pares clave-valor). Esto se basa en una compensación: no quiero que se unan comandos separados solo porque son algún tipo de entrada entre sí. Dentro de CommandB, no me importa cómo se configuró UserName, solo que está ahí para que lo use. Lo mismo en CommandA: configuro la información, no quiero saber qué están haciendo otros con ella, ni quiénes son.
Esto significa una especie de contexto pasajero, que puedes encontrar malo. Para mí, la alternativa es peor, especialmente si este contexto clave-valor simple (puede ser un simple bean con getters y setters, para limitar un poco el factor de "forma libre") puede permitir que la solución sea simple y comprobable, con comandos separados, cada uno con su propia lógica de negocios.
fuente
Supongamos que tiene una interfaz de comando:
Y un tema:
Lo que necesitas es:
Establecer
NameObserver& o
como referencia a CommandB. Ahora, cuando CommandA cambia, el nombre de Sujetos CommandB puede ejecutarse con la información correcta. Si el nombre es usado por más comandos, use unstd::list<NameObserver>
fuente
No sé si esta es la forma correcta de manejar esto en los Programadores (en cuyo caso me disculpo), pero, después de haber revisado todas las respuestas aquí (@ Frank en particular). Refactoré mi código de esta manera:
Gracias por la ayuda y espero no haber cometido errores aquí :)
fuente