Eu tenho essa implementação antiga do padrão de comando. É meio que passar um Contexto por toda a implementação da DIOperation , mas percebi mais tarde, no processo de aprendizado e aprendizado (que nunca para), que não é o ideal. Eu também acho que a "visita" aqui realmente não se encaixa e apenas confunde.
Na verdade, estou pensando em refatorar meu código, também porque um Comando não deve saber nada sobre os outros e, no momento, todos compartilham os mesmos pares de valores-chave. É realmente difícil manter qual classe possui qual valor-chave, às vezes levando à duplicação de variáveis.
Um exemplo de caso de uso: digamos que CommandB exija UserName, definido por CommandA . CommandA deve definir a chave UserNameForCommandB = John ? Ou eles devem compartilhar um valor-chave comum UserName = John ? E se o UserName for usado por um terceiro comando?
Como posso melhorar esse design? Obrigado!
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;
};
fonte
Respostas:
Estou um pouco preocupado com a mutabilidade dos seus parâmetros de comando. É realmente necessário criar um comando com parâmetros em constante mudança?
Problemas com sua abordagem:
Deseja que outros threads / comandos alterem seus parâmetros enquanto
perform
está acontecendo?Deseja
visitBefore
evisitAfter
do mesmoCommand
objeto ser chamado comDIParameter
objetos diferentes ?Deseja que alguém alimente parâmetros aos seus comandos, dos quais os comandos não têm idéia?
Nada disso é proibido pelo seu design atual. Embora um conceito genérico de parâmetro de valor-chave tenha seus méritos às vezes, não gosto disso em relação a uma classe de comando genérica.
Exemplo de consequências:
Considere uma realização concreta de sua
Command
classe - algo assimCreateUserCommand
. Agora, obviamente, quando você solicita a criação de um novo usuário, o comando precisará de um nome para esse usuário. Dado que eu sei que osCreateUserCommand
e asDIParameters
aulas, qual parâmetro devo definir?Eu poderia definir o
userName
parâmetro, ouusername
... você trata os parâmetros sem distinção de maiúsculas e minúsculas? Eu realmente não saberia ... oh espera ... talvez seja sóname
?Como você pode ver a liberdade que você ganha com um mapeamento genérico de valores-chave, implica que o uso de suas classes como alguém que não os implementou é injustificadamente difícil. Você precisaria pelo menos fornecer algumas constantes para seus comandos, para que outras pessoas saibam quais chaves são suportadas por esse comando.
Possíveis abordagens de projetos diferentes:
Parameter
instâncias imutáveis, você pode reutilizá-las livremente entre diferentes comandos.UserParameter
classe que contém exatamente os parâmetros que eu precisaria para comandos que envolvem um usuário, seria muito mais simples trabalhar com essa API. Você ainda pode ter herança nos parâmetros, mas não faria mais sentido que as classes de comando usassem parâmetros arbitrários - do lado profissional, é claro que isso significa que os usuários da API sabem exatamente quais parâmetros são necessários.visitBefore
evisitAfter
, embora também os reutilize com parâmetros diferentes, você estará aberto ao problema de ser chamado com parâmetros diferentes. Se os parâmetros devem ser os mesmos em várias chamadas de método, é necessário encapsulá-los no comando para que eles não possam ser alternados para outros parâmetros entre as chamadas.fonte
O que é legal nos princípios de design é que, mais cedo ou mais tarde, eles entram em conflito.
Na situação descrita, acho que prefiro ir com algum tipo de contexto em que cada comando possa obter e colocar informações (principalmente se forem pares de valores-chave). Isso se baseia em uma troca: não quero que comandos separados sejam acoplados apenas porque são algum tipo de entrada um para o outro. Dentro do CommandB, eu não me importo como o UserName foi definido - apenas existe para eu usar. A mesma coisa no CommandA: coloquei as informações, não quero saber o que os outros estão fazendo com elas - nem quem são elas.
Isso significa um tipo de contexto passageiro, que você pode achar ruim. Para mim, a alternativa é pior, especialmente se esse contexto simples de valor-chave (pode ser um bean simples com getters e setters, para limitar um pouco o fator "forma livre") pode permitir que a solução seja simples e testável, com comandos separados, cada um com sua própria lógica de negócios.
fonte
Vamos supor que você tenha uma interface de comando:
E um assunto:
O que você precisa é:
Defina
NameObserver& o
como uma referência ao CommandB. Agora, sempre que o CommandA mudar, o nome do sujeito CommandB poderá ser executado com as informações corretas. Se o nome for usado por mais comando, use umstd::list<NameObserver>
fonte
Não sei se essa é a maneira correta de lidar com isso nos programadores (nesse caso, peço desculpas), mas depois de ter verificado todas as respostas aqui (@ do Frank em particular). Refatorei meu código dessa maneira:
Obrigado pela ajuda e espero não ter cometido erros aqui :)
fonte