Design Pattern Strategy: Navegando pelas Estratégias do Desenvolvimento de Software

O padrão Strategy é um pattern de design do tipo comportamental, seu funcionamento é baseado em criar uma família de algoritmos que possam ser trocados em tempo de execução, sem que haja necessidade de alterar o código, basicamente as estratégias são criadas e a depender do fluxo do sistema, é executado um algoritmo ou outro.

Esse tipo de padrão acaba sendo muito útil quando nosso sistema tem que lidar com tomadas de decisão, pois podemos definir execuções para cada informação que for imputada no sistema.

Implementação

A implementação desse design é relativamente simples, primeiro passo vamos precisar de um contrato para ser seguido pelas nossas futuras implementações, então vamos definir uma interface.

public interface Estrategia {

	void executar();

}
Estrategia.java

Todos os algoritmos que vamos implementar a seguir precisam implementar essa interface “Estrategia”, para ter um ponto comum de execução vamos criar uma classe que encapsula a execução das nossas estratégias e que criará uma referência para que seja possível trocar dinamicamente a estratégia utilizada, vamos chamá-la de Contexto.

public class Contexto {

	private Estrategia estrategia;
	
	public void setStrategy(Estrategia estrategia) {
		this.estrategia = estrategia;
	}
	
	public void executar() {
		if(estrategia != null) {
			estrategia.executar();
		} else {
			throw new IllegalArgumentException("Nenhuma estratégia definida");
		}
	}
}
Contexto.java

Lembrando que a nomenclatura que estamos utilizando é simplesmente para ser didática, nenhum design pattern exige nomenclatura, os nomes de classes e interfaces devem seguir uma lógica mais condizente com a sua aplicação e regra de negócio.

Bom, agora temos nosso contrato e quem irá executar as nossas estratégias, agora vamos implementar duas estratégias para demonstrar a utilização.

public class EstrategiaA implements Estrategia {

	@Override
	public void executar() {
		System.out.println("Executando estratégia A");
	}

}
EstrategiaA.java
public class EstrategiaB implements Estrategia {

	@Override
	public void executar() {
		System.out.println("Executando estratégia B");
	}

}
EstrategiaB.java

Testando a implementação

Nosso padrão está pronto, podemos testar utilizando utilizando uma implementação simples que pode demonstrar a escolha de algoritmos diferentes em tempo de execução.

public class ExemploStrategy {
	
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		
		System.out.print("Escolha a estratégia a ser utilizada: ");
		String estrategiaEscolhida = scanner.nextLine().toUpperCase().trim();
		
		Estrategia estrategia = null;
		
		switch(estrategiaEscolhida) {
		 	case "A":
		 		estrategia = new EstrategiaA();
		 		break;
		 	case "B":
		 		estrategia = new EstrategiaB();
		 		break;
		}
		
		Contexto contexto = new Contexto();
		contexto.setStrategy(estrategia);
		contexto.executar();
		
		scanner.close();
	}
}
ExemploStrategy.java

Observe que a depender do input que é feito no nosso sistema é escolhido o algoritmo a ser utilizado, e também esse tipo de implementação torna nosso sistema bem escalável, pois a cada nova feature precisamos apenas adicionar uma nova estratégia.

Observações a respeito do pattern Strategy

Como todo design pattern o Strategy também pode nos apresentar alguns problemas ao utilizá-lo, um dos principais é a complexidade inicial, pois definir uma interface que irá abranger todas as possíveis estratégias que serão utilizadas é bem complexo de se pensar e o mais comum de acontecer é lá na frente com a maturidade do sistema você identificar que uma das estratégias não se encaixa neste contrato e ter que criar desvios no sistema.

Esse tipo de padrão pode gerar um número excessivo de classes a depender da complexidade do seu sistema, pois quanto maior crescer o número de estratégias a serem utilizadas, o mesmo acontece com as classes, isso pode tornar o código um pouco grande.

Embora esse design tenha baixo acoplamento, é possível que você tenha um alto acoplamento com a classe que vai selecionar a estratégia a ser utilizada, pois para o seu funcionamento é necessário que ela conheça todas as estratégias.

A depender da evolução do seu sistema, se ao longo do tempo for crescer muito o número de estratégias ou se precisar refatorar muito as estratégias que já foram criadas, esse tipo de padrão pode tornar a refatoração mais complexa, então é bom avaliar com critério em quais pontos do sistema ele deve ser implementado.

Conclusão

Sem dúvida o Strategy é um excelente padrão para utilização quando nosso sistema tem que reagir com diferentes algoritmos ao uma decisão sistema, além de escalável ele permite um baixo acoplamento na sua utilização tornando o código mais simples de ler e também de dar manutenção, e proporcionando uma segregação de responsabilidades entre as classes bem interessante.

Como todo código a seus prós e contras quando utilizado, mas é um padrão a se considerar principalmente em sistemas grandes, saber escolher a hora certa de implementá-lo é a chave.

Links uteis

Mauricio Lima
Mauricio Lima

Bacharel em Ciência da Computação, profissional dedicado ao desenvolvimento de software e entusiasta da tecnologia.

Artigos: 65