Quanta lógica pode ser colocada em um comando? Ou de outra maneira: para que tipo de lógica é o padrão de comando?

8

Uso o padrão de comando há algum tempo, mas nunca tenho muita certeza da quantidade de lógica que posso colocar no Executemétodo.

Minha implementação atual do padrão de comando é semelhante a esta:

public abstract class Command
{
    public static event EventHandler Completed = delegate { };        

    public bool Success { get; private set; }

    public Exception Exception {get; private set; }

    public abstract bool Execute();

    protected bool OnCompleted(bool success, Exception ex = null)
    {       
        Success = success;
        Exception = ex;
        Completed(this, EventArgs.Empty)
        return success;
    }
}

e estas são as perguntas que me faço (e pratico em meus comandos):

  1. É bom mostrar caixas de mensagens ou caixas de diálogo de arquivo etc.?
  2. É possível definir propriedades de qualquer objeto?
  3. Um comando pode conter lógica de negócios?
  4. Um comando pode modificar os controles da GUI de qualquer maneira?
  5. Quais comandos de camada pertencem? Visualizar ou camada de dados? Posso ter comandos nas duas camadas?
  6. Um comando pode fazer tudo o que anteriormente estava no button1_Click?
  7. Um comando deve ser testado por unidade?
  8. Um comando pode ser visto como um usuário que faz uso das APIs e que cria a última camada de um aplicativo e também o último recurso para capturar exceções?
  9. Os comandos podem ser executados também por código (o comando chama api, o api executa e, por fim, outro API chama o comando) ou apenas o usuário pode invocá-lo e as APIs não devem saber da existência dele?
  10. Existe um lugar para comandos no MVC ou MVVC ou qualquer outro padrão de design com um controlador? Eles parecem ser mutuamente exclusivos. O que é preferível?

Existem vários tutoriais mostrando como implementar o padrão de comando, mas nenhum discute como aplicá-lo em um aplicativo real.

t3chb0t
fonte

Respostas:

7

Geralmente, o Padrão de Comando é usado para separar O QUE da OMS e O QUE QUANDO. Este é o benefício de ter uma interface simples tão simples quanto:

public abstract class Command {
    public abstract void execute();
}

Vamos imaginar que você tem uma aula EngineOnCommand. Você pode passar este comando para outros objetos que aceitam instâncias de comando. Portanto, isso significa que a classe que recebe esse EngineOnCommand também pode receber um comando diferente a ser executado por ele e nunca deve conhecê-lo. Isso significa que você separou O QUE da OMS .

Agora, o segundo caso para o Comando Patterm é dissociar O QUE de QUANDO . Vamos imaginar que você deseja criar um sistema no qual as ações no banco de dados sejam executadas apenas à noite, mas seguindo a sequência em que foram solicitadas. Com uma fila de comandos na qual você pode executar um por um, você poderia ter conseguido isso. A idéia é que, na verdade, a invocação do comando aciona um enfileiramento do comando em uma lista para ser executado posteriormente.

Espero que meus exemplos acima ajudem a entender qual é a ideia do padrão. Agora tentarei responder a algumas de suas perguntas usando o acima como base.

Um comando pode conter lógica de negócios?

sim, acredito que deveria conter. Ele o conterá de maneira dissociada da OMS e QUANDO.

Um comando pode modificar os controles da GUI de qualquer maneira?

Isso significa que você está acoplando à GUI. Isso significa que não está sendo usado com a intenção de dissociar O QUE da OMS. O comando idealmente não deve saber quem o está chamando, se é uma GUI ou uma funcionalidade em lote.

Um comando deve ser testado por unidade?

Não deve, deve ser testável por unidade . Todo o código deve ser testado por unidade, mas, idealmente, todos os comandos devem ser testados por unidade.

Desculpe por não responder a todas as suas perguntas, mas acredito que você deve verificar o livro do GOF . Ele contém alguns bons exemplos.

pietromena
fonte
+1 Excelente resposta, apesar de um pequeno detalhe: defender uma determinada prática, como teste de unidade, é ótimo, mas comandar essas práticas como se fossem universalmente obrigatórias tende a não ser útil para jovens programadores. Falando por experiência própria.
Phil
2

A maior parte dos benefícios dos comandos é que eles facilitam desfazer uma ação, refazer uma ação, executar uma ação em vários locais (por uma conexão de rede) ou executá-la posteriormente, e assim por diante.

Com aquilo em mente:

  1. Provavelmente não. É difícil imaginar qualquer situação em que "desfazer" uma caixa de diálogo faça sentido, e esse é o tipo de coisa que eu prefiro colocar no código da interface do usuário.

  2. Desde que esse objeto esteja acessível de qualquer lugar que queira executar o comando, é claro que é bom alterar suas propriedades.

  3. Absolutamente. Isso está relacionado ao fato de seu modelo "conter" a lógica de negócios; é considerado um antipadrão ("modelo de domínio anêmico") ter muito pouca lógica de negócios.

  4. Se os controles da GUI fazem parte do seu modelo, absolutamente. Caso contrário, é questionável.

  5. Definitivamente não é a vista. A visualização deve ser informada de que os dados ou o modelo foram alterados e reagem de acordo, mas a mudança real deve ocorrer nos dados ou no modelo.

  6. Em princípio, provavelmente sim. Ser capaz de desfazer comandos é uma das melhores coisas sobre o padrão.

  7. A função execute () do comando definitivamente deve ser testada em unidade. Novamente, se os comandos estiverem vinculados a um modelo de algum tipo, eles estarão entre as partes mais fáceis de um aplicativo para teste.

  8. Não tenho muita certeza do que você quer dizer, mas:

    • É perfeitamente adequado para uma API externa usar comandos como entrada ou saída. Supondo que você os valide, é claro.
    • "última camada" parece uma visualização para mim. No MVC, os comandos provavelmente devem ser gerados pelo controlador e manipulados pelo modelo, por isso seria sensato dizer que a exibição é construída "acima" do material do comando, se é isso que você estava procurando.
    • Quanto às exceções, provavelmente não. No MVC, eu esperaria que o controlador capturasse as exceções e as transformasse no tipo certo de caixa de diálogo. Não é o modelo / comandos.
  9. Absolutamente. A maioria dos benefícios dos comandos é praticamente impossível de implementar se o código nunca os executa.

  10. Provavelmente já é óbvio agora, mas não os vejo como mutuamente exclusivos. Na minha equipe de trabalho, o controlador converte eventos gerados pelo usuário em comandos, o modelo recebe e executa os comandos (e salva os comandos "desfazer" em algum lugar) e, quando termina, o controlador diz à exibição para atualizar-se com base no novo modelo . Os comandos também são enviados pela rede para quaisquer cópias do modelo que estão sendo visualizadas por outros usuários, para que eles também o vejam. Funciona de forma brilhante.

E a pergunta do título: quanta lógica colocar em um comando? Eu não colocaria mínimo ou máximo nisso. O que eu diria é que é uma idéia muito boa implementar comandos mais complexos usando uma série de comandos mais simples e refatorar comandos da mesma maneira que você refatoraria as funções.

Ixrec
fonte