Usando um padrão de estratégia e um padrão de comando

121

Os dois padrões de design encapsulam um algoritmo e separam os detalhes da implementação de suas classes de chamada. A única diferença que posso discernir é que o padrão de estratégia usa parâmetros para execução, enquanto o padrão de comando não.

Parece-me que o padrão de comando exige que todas as informações para execução estejam disponíveis quando criadas, e é capaz de atrasar sua chamada (talvez como parte de um script).

Que determinações orientam se deve usar um padrão ou outro?

Extrakun
fonte

Respostas:

94

Estou incluindo uma tabela de hierarquia de encapsulamento de vários padrões de design do GoF para ajudar a explicar as diferenças entre esses dois padrões. Espero que ilustre melhor o que cada um encapsula, para que minha explicação faça mais sentido.

Primeiro, a hierarquia lista o escopo para o qual um determinado padrão é aplicável ou o padrão apropriado a ser usado para encapsular algum nível de detalhe, dependendo do lado da tabela em que você inicia.

tabela de hierarquia de encapsulamento de padrão de design

Como você pode ver na tabela, um objeto Padrão de estratégia oculta detalhes da implementação de um algoritmo, portanto, o uso de um objeto de estratégia diferente executará a mesma funcionalidade, mas de uma maneira diferente. Cada objeto de estratégia pode ser otimizado para um fator específico ou operar em algum outro parâmetro; e, através do uso de uma interface comum, o contexto pode trabalhar com segurança.

O padrão de comando encapsula um nível de detalhe muito menor do que um algoritmo. Ele codifica os detalhes necessários para enviar uma mensagem a um objeto: receptor, seletor e argumentos. O benefício de objetivar uma parte tão pequena da execução do processo é que essas mensagens podem ser invocadas em diferentes pontos do tempo ou local de maneira geral, sem a necessidade de codificar seus detalhes. Ele permite que as mensagens sejam invocadas uma ou mais vezes ou transmitidas para diferentes partes do sistema ou para vários sistemas sem exigir que os detalhes de uma chamada específica sejam conhecidos antes da execução.

Como é típico para os padrões de design, eles não exigem que todas as implementações sejam idênticas em detalhes para exibir o nome do padrão. Os detalhes podem variar na implementação e em quais dados são codificados no objeto versus como argumentos do método.

Huperniketes
fonte
Portanto, se eu tivesse um sistema que filtrasse os resultados com um "pipeline de filtro" e usasse delegados como filtros (onde cada um dos algoritmos do filtro seria encapsulado em uma função), isso seria considerado um padrão de comando? Nesse caso, vejo o delegado para a função de filtro como fornecendo um tipo de contrato para o que cada filtro deve aderir em termos de entrada e saída.
KTF
4
@KTF, não. O padrão Comando emprega um objeto que possui a maioria (se não todas) as informações necessárias (por exemplo, receptor, seletor, argumentos) para chamar o método de um objeto. É um padrão simplista que pode ser usado em outros padrões de design, como Cadeia de Responsabilidade, Coleção e o padrão de Pipeline que você descreve. O "contrato das sortes" fornecido por seus delegados é outro padrão, a Interface.
Huperniketes
50

Estratégias encapsulam algoritmos. Os comandos separam o remetente do destinatário de uma solicitação, transformam uma solicitação em um objeto.

Se for um algoritmo, como algo será feito, use uma estratégia. Se você precisar separar a chamada de um método da sua execução, use um comando Os comandos geralmente são usados ​​quando você enfileira mensagens para uso posterior, como uma tarefa ou transação.

Paul Rubel
fonte
isso fazia sentido en.wikipedia.org/wiki/Command_Pattern cliente e invocador estão ligados, mas ao mesmo tempo, eles não se conhecem!
Kalpesh Soni
26

Respondendo a uma pergunta muito antiga. (alguém está vendo as últimas respostas em vez das mais votadas?)

É uma confusão válida ter por causa das semelhanças. Os padrões de estratégia e comando utilizam encapsulamento . Mas isso não os torna iguais.

A principal diferença é entender o que está encapsulado. O princípio OO, ambos os padrões dependem, é encapsular o que varia .

Em caso de estratégia, o que varia é o algoritmo . Por exemplo, um objeto de estratégia sabe como gerar um arquivo XML, enquanto o outro gera, por exemplo, JSON. Diferentes algoritmos são mantidos ( encapsulados ) em diferentes classes. É tão simples quanto isso.

Em caso de comando, o que varia é a própria solicitação . Pedido pode vir de File Menu > Deleteou Right Click > Context Menu > DeleteouJust Delete Button pressed . Todos os três casos podem gerar 3 objetos de comando do mesmo tipo. Esses objetos de comando representam apenas 3 pedidos de exclusão; não algoritmo de exclusão. Como as solicitações são um monte de objetos agora, podemos gerenciá-las facilmente. De repente, torna-se trivial fornecer funcionalidades como desfazer ou refazer.

Não importa como o comando implementa a lógica solicitada. Ao chamar execute (), ele pode implementar um algoritmo para acionar a exclusão ou até mesmo delegá-lo a outros objetos, pode até delegar a uma estratégia. É apenas detalhes de implementação do padrão de comando. É por isso que é nomeado como comando, embora não seja uma maneira educada de solicitar : -)

Compare isso com a estratégia; esse padrão está relacionado apenas à lógica real que é executada. Se fizermos isso, ajudará a obter diferentes combinações de comportamentos com um conjunto mínimo de classes, evitando a explosão de classes.

Penso que o Command nos ajuda a ampliar nossa compreensão do encapsulamento, enquanto a Strategy fornece o uso natural do encapsulamento e do polimorfismo.

rpattabi
fonte
15

A maneira que eu vejo é que você tem várias maneiras de fazer a mesma coisa, cada uma delas é uma estratégia, e algo em tempo de execução determina qual estratégia será executada.

Talvez primeiro tente StrategyOne, se os resultados não forem bons o suficiente, tente StrategyTwo ...

Os comandos estão vinculados a coisas distintas que precisam acontecer como TryToWalkAcrossTheRoomCommand. Este comando será acionado sempre que algum objeto tentar atravessar a sala, mas, dentro dele, poderá tentar o StrategyOne e o StrategyTwo para tentar atravessar a sala.

Marca

MStodd
fonte
2
RE: "várias maneiras de fazer a mesma coisa" - isso parece entrar em conflito com alguns dos exemplos comuns de estratégia. Especificamente aqueles onde existem classes de implementação que fazem adição, subtração, multiplicação, etc. Talvez esses não sejam bons exemplos?
Joshua Davis
1
@ JoshuaDavis todas essas "substratagies" são casos especiais de uma estratégia: operação aritmética . eles têm argumentos comuns (2 operandos) e produzem um valor como resultado. praticamente fazendo a mesma coisa (sendo caixas pretas) de maneira diferente, dependendo da implementação. Então eu vejo nenhum conflito aqui, mas, muito pelo contrário: nice exemplo =)
jungle_mole
7

Posso estar errado na minha opinião, mas trato o comando como função a executar ou reação. Deve haver pelo menos dois jogadores: aquele que solicita a ação e aquele que executa a ação. A GUI é um exemplo típico de padrão de comando:

  • Todos os botões na barra de ferramentas do aplicativo estão associados a alguma ação.
  • Button é o executor neste caso.
  • Ação é o comando neste caso.

O comando geralmente é limitado a algum escopo ou área de negócios, mas não é necessário: você pode ter comandos que emitem uma fatura, iniciam um foguete ou removem um arquivo implementando a mesma interface (por exemplo, execute()método único ) em um aplicativo. Geralmente, os comandos são auto-suficientes e, portanto, não precisam de nada do executor para processar a tarefa a que se destinam (todas as informações necessárias são fornecidas no momento da construção); às vezes, os comandos são sensíveis ao contexto e devem poder descobrir esse contexto. (O comando Backspace deve conhecer a posição do sinal de intercalação no texto para remover corretamente o caractere anterior; o comando Rollback deve descobrir a transação atual para reversão; ...).

A estratégia é um pouco diferente: está mais ligada a alguma área. A estratégia pode definir uma regra para formatar uma data (no UTC? Locale specific?) (Estratégia "formatador de data") ou para calcular um quadrado para uma figura geométrica (estratégia "calculadora quadrada"). As estratégias são, nesse sentido, objetos com peso de mosca, que recebem algo como entrada ("data", "figura", ...) e tomam alguma decisão com base. Talvez não seja o melhor, mas um bom exemplo de estratégia é aquele conectado à javax.xml.transform.Sourceinterface: dependendo se o objeto transmitido é DOMSourceou SAXSourceou StreamSourcea estratégia (= transformador XSLT neste caso) aplicará regras diferentes para processá-lo. A implementação pode ser simples switchou envolver um padrão de Cadeia de responsabilidade .

Mas, de fato, há algo em comum entre esses dois padrões: comandos e estratégias encapsulam algoritmos dentro da mesma área semântica.

dma_k
fonte
1
Trato o comando como uma função de retorno de chamada ou reação. Deveria haver pelo menos dois jogadores: um que solicita a ação e outro que executa ... - Entendo o que você está tentando dizer, mas evito usar a palavra 'retorno de chamada', porque muitas vezes a palavra 'callback' implica uma chamada assíncrona e você não precisa fazer invocações assíncronas para que o padrão de comando seja útil. Caso em questão: Microsoft Word. Toolbar cliques de botão e prensas de teclas de atalho não chamar comandos assíncronos, mas podemos apreciar como o padrão de comando seria útil neste caso
Jim G.
Concordo, embora, como Jim disse, eu editaria para remover a referência ao retorno de chamada.
JARC
Obrigado, eu fiz algumas extensões. Deixe-me saber, se você concorda / discorda.
dma_k
5

Comando:

Componentes básicos:

  1. O comando declara uma interface para comandos abstratos comoexecute()
  2. O receptor sabe como executar um comando específico
  3. O invocador mantém o ConcreteCommand , que deve ser executado
  4. Cliente cria ConcreteCommand e atribui Receiver
  5. ConcreteCommand define ligação entre Command e Receiver

Fluxo de trabalho:

O cliente chama Invoker => Invoker chama ConcreteCommand => ConcreteCommand chama o método Receiver , que implementa resumo método Command .

Vantagem : o cliente não sofre alterações no comando e no receptor. O Invoker fornece acoplamento flexível entre o Cliente e o Destinatário. Você pode executar vários comandos com o mesmo Invoker.

O padrão de comando permite executar um comando em diferentes Receptores usando o mesmo Invoker . O invocador não tem conhecimento do tipo de receptor

Para uma melhor compreensão dos conceitos, ter um olhar para este JournalDev artigo por Pankaj Kumar e dzone artigo por James Sugrue , além de ligação Wikipedia.

Você pode usar o padrão de comando para

  1. Desacoplar o invocador e o destinatário do comando

  2. Implementar mecanismo de retorno de chamada

  3. Implementar a funcionalidade desfazer e refazer

  4. Manter um histórico de comandos

java.lang.Threadé uma boa implementação do padrão de comando . Você pode tratar o Thread como invocador e classe implementando Runnable como ConcreteCommonad / Receiver e o run()método como Command .

A versão Desfazer / Refazer do padrão de comando pode ser lida no artigo de Theodore Norvell

Estratégia:

O padrão de estratégia é muito simples de entender. Use esse padrão quando

Você tem várias implementações para um algoritmo e a implementação do algoritmo pode mudar no tempo de execução, dependendo de condições específicas .

Veja um exemplo do componente de tarifa do sistema de reservas de companhias aéreas

As companhias aéreas gostariam de oferecer tarifas diferentes durante diferentes períodos de tempo - meses de pico e fora de pico. Durante os dias de viagem fora do horário de pico, ele gostaria de estimular a demanda oferecendo descontos atraentes.

Principais conclusões do padrão de estratégia :

  1. É um padrão comportamental
  2. É baseado em delegação
  3. Altera as tripas do objeto, modificando o comportamento do método
  4. É usado para alternar entre a família de algoritmos
  5. Altera o comportamento do objeto em tempo de execução

Postagens relacionadas com exemplos de código:

Usando o padrão Command Design

Exemplo do mundo real do padrão de estratégia

Ravindra babu
fonte
0

Para mim, a diferença é uma das intenções. As implementações de ambos os padrões são bastante semelhantes, mas têm finalidades diferentes:

  • Para uma estratégia, o componente que usa o objeto sabe o que o objeto faz (e o utilizará para executar parte de seu próprio trabalho), mas não se importa com o que faz.

  • Para um comando, o componente que usa o objeto não sabe o que o comando faz nem como ele faz - apenas sabe como invocá-lo. A tarefa do chamador é apenas executar o comando - o processamento realizado pelo Comando não faz parte do trabalho principal do chamador.

Essa é a diferença - o objeto que usa o componente realmente sabe ou se importa com o que o componente faz? Na maioria das vezes, isso pode ser determinado com base no fato de o objeto padrão retornar um valor ao seu invocador. Se o invocador se importa com o que o objeto padrão faz, provavelmente desejará que ele retorne algo e será uma Estratégia. Se ele não se importa com nenhum valor de retorno, provavelmente é um comando (observe, algo como um Java Callable ainda é um comando porque, embora retorne um valor, o chamador não se preocupa com o valor - apenas o repassa ao que originalmente forneceu o comando).

BarrySW19
fonte