C# - Padrão Comportamental Gof - Strategy
Neste artigo vou apresentar o padrão comportamental Gof Strategy. |
O padrão de design
Strategy define uma família de algoritmos, encapsula cada um e os torna
intercambiáveis.
Vamos entender o
que isso significa:
- Família de algoritmos:
Isso significa que este padrão fornece um conjunto de algoritmos usando um dos
quais em tempo de execução você pode obter a saída desejada.
- Encapsula cada um :
Este padrão permite que você coloque seus algoritmos em classes diferentes
(encapsulando-os)
- Torna o algoritmo intercambiável:
A beleza do padrão Strategy é que podemos selecionar em tempo de execução qual
algoritmo queremos aplicar ao nosso objeto e também substituí-los um pelo outro.
Dessa
forma esse padrão permite que o algoritmo varie independentemente dos clientes
que o utilizam. E assim podemos dizer que o objetivo deste padrão é encapsular
um algoritmo em um objeto e fornecer interfaces genéricas o suficiente para
suportar uma variedade de algoritmos e facilitar a escolha e troca (intercâmbio)
de algoritmos criados com uma mesma função.
Este padrão é frequentemente usado em várias estruturas para fornecer aos usuários uma
maneira de alterar o comportamento de uma classe sem estendê-la.
Strategy : Exemplo
Vejamos um exemplo simples para entendermos a atuação do padrão.
Vamos supor que
temos uma tarefa a resolver :
E que descobrimos que podemos ter 3 soluções para resolver a tarefa:
Solucão1 , 2 e 3
Em cada solução temos representados um comportamento e um algoritmo diferente.
Aqui temos a família de algoritmo encapsulada cada um em sua classe.
De acordo com o padrão Strategy, a solução
que deverá ser usada será decidida pelo cliente apenas em tempo de
execução. Assim, o cliente decidirá se usará a
Solução 1 , 2 ou 3 para realizar a tarefa em tempo de execução.
Então temos que as
soluções que representam a família de algoritmos encapsuladas cada uma em sua
classe
podem ser intercambiáveis e podemos selecionar qual usar em tempo de
execução substituindo um pelo outro.
Tomando um exemplo
do mundo real, temos que os meios de transporte para um aeroporto são um exemplo
do padrão Strategy ou Estratégia.
Existem várias opções de transporte que podem ser usadas neste caso :
- pegar um ônibus;
- usar um carro;
- pegar um táxi;
Qualquer um desses meios de transporte leva o viajante ao aeroporto e pode ser
usado de forma intercambiável.
O viajante deve escolher a Estratégia com base nas compensações entre custo,
conveniência e tempo.
Diagrama UML
O diagrama UML do padrão Strategy segundo o Gof apresenta os seguintes participantes
1- Strategy
- Define a
Interface comum para todas as classes (variações concretas) que definem os
diversos comportamentos esperados;
- O Context usa esta interface para chamar o
algoritmo definido por um ConcreteStrategy;
2- ConcreteStrategy -
- Classes que
implementam os algoritmos que devem atender a cada contexto;
3- Context -
-Classe onde os
objetos ConcreteStrategy serão instanciados;
-Mantém uma referência para o objeto Strategy
Assim temos aqui:
- Uma interface chamada Strategy que define um contrato a ser implementado;
- As classes de estratégia concretas A,B e C que implementam essa interface;
- Por último, a classe Context através da qual
podemos usar diferentes estratégias concretas em tempo de execução.
As classes Context instanciam os objetos Strategy e invocam o método
AlgorithmInterface passando os parâmetros
solicitados;
A interface Strategy decide qual das implementações
ConcreteStrategy deve atender a chamada;
Quando podemos usar o padrão
Podemos usar o padrão Strategy quando :
- Muitas classes
relacionadas diferem apenas em seu comportamento;
- Precisamos usar diferentes variantes de um algoritmo;
- Um algoritmo usa dados que os clientes não deveriam conhecer; Neste caso
usamos o padrão Strategy para evitar a exposição de estruturas de dados
complexas e específicas do algoritmo.
- Uma classe define muitos comportamentos e eles aparecem como várias instruções
condicionais em suas operações;
Aqui em vez de muitos condicionais, podemos mover os condicionais relacionados
para sua própria classe de Estratégia.
- Você tem um método que é aplicado em diferentes situações nas quais é exigido
um comportamento específico;
Vantagens do padrão
Como vantagens deste padrão temos que :
-Facilita e
simplifica os testes de unidade pois cada algoritmo tem sua classe e pode ser
testado pela sua própria interface;
- Evita o uso de condicionais, tornando o código mais flexível e fácil de
estender;
- É aderente aos princípios de alta coesão e baixo acoplamento;
- Orienta a programar para uma interface e usar a composição;
Desvantagem
E como desvantagem
podemos citar que :
- Ocorre um aumento no número de classes;
-A aplicação deve estar ciente de todas as estratégias para poder selecionar a
estratégia certa para a situação certa;
Aplicação prática do padrão
Para mostrar um
exemplo de implementação deste padrão vamos supor que eu quero poder compactar
arquivos, e, para isso eu tenho 3 soluções que são as estratégias que eu posso
usar para realizar essa tarefa.
Assim eu posso compactar arquivos usando o formato Rar,
o formato ZIP e o formato GZip.
Assim vamos usar o padrão Strategy para definir como podemos implementar 3
algoritmos de forma que possamos escolher o formato em tempo de execução.
Implementação prática
Levando em conta este cenário vamos implementar o padrão Strategy usando uma aplicação Console .NET Core (.NET 5.0) criada no VS 2019 Community.
A seguir temos o diagrama de classes obtido a partir do VS 2019 na implementação do padrão:
A seguir temos o código usado na implementação:
1- A interface ICompressao
//Strategy public interface ICompressao { void ComprimirArquivo(string nomeArquivo); } |
2- A classe CompressaoContext
//Context
public class CompressaoContext
{
private ICompressao _icompressao;
public CompressaoContext(ICompressao icompressao)
{
_icompressao = icompressao;
}
public void DefineStrategy(ICompressao icompressao)
{
_icompressao = icompressao;
}
public void CriarArquivoCompactado(string nomeArquivo)
{
_icompressao.ComprimirArquivo(nomeArquivo);
}
}
|
3- Classe CompressaoZip
public class CompressaoGzip : ICompressao { public void ComprimirArquivo(string nomeArquivo) { Console.WriteLine($"\nO arquivo {nomeArquivo} foi compactado usando GZip " + $"\nUm arquivo com extensão .gzip foi criado"); } } |
4- Classe CompressaoGzip
public
class CompressaoZip : ICompressao { public void ComprimirArquivo(string nomeArquivo) { Console.WriteLine($"\nO arquivo '{nomeArquivo}' foi compactado usando Zip " + $"\nUm arquivo com extensão .zip foi criado"); } } |
5- Classe CompressaoRar
public
class CompressaoRar : ICompressao { public void ComprimirArquivo(string nomeArquivo) { Console.WriteLine($"\nO arquivo '{nomeArquivo}' foi compactado usando Rar. " + $"\nUm arquivo com extensão .rar foi criado"); } } |
7- Program
|
A execução do projeto irá apresentar o seguinte resultado:
Desta forma com o
padrão Strategy é possível reutilizar o código por todo o projeto.
Pegue o código
do projeto aqui :
StrategyExemplo.zip
"Bendito seja o
Deus e Pai de nosso Senhor Jesus Cristo, Pai das misericórdias e Deus de toda
consolação,
que nos consola em todas as nossas tribulações, para que, com a consolação que
recebemos de Deus, possamos consolar os que estão passando por tribulações."
2 Coríntios 1:3,4
Referências:
NET - Unit of Work - Padrão Unidade de ...
NET - O padrão de projeto Decorator
NET - Padrão de Projeto Builder
C# - O Padrão Strategy (revisitado)
NET - O padrão de projeto Command
NET - Apresentando o padrão Repository