Sei que essa é uma pergunta antiga, mas acho que tenho outro exemplo interessante que implementei recentemente.
Este é um exemplo muito prático do padrão de estratégia usado em um sistema de entrega de documentos.
Eu tinha um sistema de entrega de PDF que recebia um arquivo contendo muitos documentos e alguns metadados. Com base nos metadados, ele decidiu onde colocar o documento; digamos, dependendo dos dados, eu poderia armazenar o documento em A
, B
ouC
sistemas de armazenamento, ou uma combinação dos três.
Diferentes clientes usavam este sistema, e eles tinham diferentes requisitos de reversão / tratamento de erros em caso de erros: um queria que o sistema de entrega parasse no primeiro erro, deixasse todos os documentos já entregues em seus depósitos, mas parasse o processo e não entregasse mais nada ; outro queria retroceder B
em caso de erros ao armazenar C
, mas deixar o que já foi entregue A
. É fácil imaginar que um terceiro ou quarto também terá necessidades diferentes.
Para resolver o problema, criei uma classe de entrega básica que contém a lógica de entrega, além de métodos para reverter coisas de todos os armazenamentos. Esses métodos não são chamados diretamente pelo sistema de entrega em caso de erros. Em vez disso, a classe usa injeção de dependência para receber uma classe "Estratégia de manipulação de rollback / erro" (com base no cliente que usa o sistema), que é chamada em caso de erros, que por sua vez chama os métodos de rollback se for apropriado para essa estratégia.
A própria classe de entrega relata o que está acontecendo para a classe de estratégia (quais documentos foram entregues a quais armazenamentos e quais falhas aconteceram) e, sempre que ocorre um erro, ela pergunta à estratégia se deve continuar ou não. Se a estratégia diz "pare", a classe chama o método "cleanUp" da estratégia, que usa as informações relatadas anteriormente para decidir quais métodos de rollback chamar da classe de entrega ou simplesmente não faz nada.
rollbackStrategy.reportSuccessA(...);
rollbackStrategy.reportFailureB(...);
if (rollbackStrategy.mustAbort()) {
rollbackStrategy.rollback(); // rollback whatever is needed based on reports
return false;
}
Portanto, agora tenho duas estratégias diferentes: uma é a QuitterStrategy
(que fecha no primeiro erro e não limpa nada) e a outra é a MaximizeDeliveryToAStrategy
(que tenta, tanto quanto possível, não abortar o processo e nunca reverter o material entregue ao armazenamento A
, mas reverte coisas de B
se a entrega C
falhar).
Do meu entendimento, este é um exemplo do padrão de estratégia. Se você (sim, você está lendo) acha que estou errado, comente abaixo e me avise. Estou curioso para saber o que constituiria um uso "puro" do padrão de estratégia e quais aspectos da minha implementação violam a definição. Acho que parece um pouco engraçado porque a interface de estratégia é um pouco gorda. Todos os exemplos que vi até agora usam apenas um método, mas ainda acho que isso encapsula um algoritmo (se uma parte da lógica de negócios pode ser considerada um algoritmo, o que eu acho que é).
Como a estratégia também é notificada sobre eventos durante a execução da entrega, ela também pode ser considerada um Observador , mas isso é outra história.
Com um pouco de pesquisa, parece que este é um "padrão composto" (como MVC, um padrão que usa vários padrões de design de uma maneira particular) chamado de Consultor . É um consultor sobre se a entrega deve continuar ou não, mas também é um gerenciador de erros ativo, pois pode reverter coisas quando solicitado.
De qualquer forma, este é um exemplo bastante complexo que pode fazer com que você sinta que os usos do padrão de estratégia são muito simples / bobos. Pode ser muito complexo e ainda mais aplicável quando usado junto com outros padrões.