Café com Design Patterns — Strategy Pattern.

Alcides Junior
6 min readNov 15, 2021

A ideia por trás do Café com Design Patterns é trazer de forma simples e direta, alguns Patterns e suas aplicabilidades.

Nesse artigo abordaremos sobre:

  • O que são Design Patterns?
  • Como eles são classificados?
  • Strategy Pattern
  • O problema
  • A solução
  • Conclusão

O que são Design Patterns?

Vamos imaginar que queremos fazer um café forte.
Para quem nunca fez café, podemos pesquisar na internet e pegar uma receita ou simplesmente tentar fazer e se tivermos sorte vamos acertar, porém, a chance de dar errado é muito grande. Podemos até fazer mas talvez não fique tão bom. Algum dia, alguém, depois de tanto fazer e metrificar as porções encontrou uma forma ideal e aplicável a qualquer quantidade, seja para uma xícara ou 1 litro de café. Essa pessoa, criou uma receita onde tem uma boa quantidade que serve bem e não desperdiça café. Essa receita foi compartilhada e hoje, sabemos a quantidade certa para fazermos um café forte.

Trazendo para o lado de engenharia de software, podemos dizer que Design Pattern é uma solução geral que visa resolver um problema que ocorre com frequência dentro de um determinado contexto em um projeto de software.

Como eles são classificados?

  • Criacionais: são os que fornecem mecanismos de criação de objetos que nos permite aumentar a flexibilidade e reutilização de código.
  • Estruturais: são os que lidam com a composição de objetos e explicam maneiras simples de montar objetos e classes em estruturas maiores, mantendo flexíveis e eficientes.
  • Comportamentais: são os que lidam em melhorar a comunicação e atribuição de responsabilidades entre objetos diferentes em um sistema.

Na diagramação abaixo, temos o esquema que contém a classificação expandida de cada pattern.

https://www.lambdatest.com/blog/wp-content/uploads/2021/03/Behavioral-Design-Patterns.png

Strategy Pattern

A idéia por trás do Strategy Pattern consiste em definir um conjunto de algoritmos, encapsular e fazer com que estes sejam intercambiáveis, ou seja, podem permutar. Esse padrão, permite que os algoritmos possam variar independentemente dos que clientes que o usam.

Vamos entender a diagramação abaixo?

- Context: chamamos de contexto, a classe que faz uso da Strategy.
- Interface: é a abstração da nossa Strategy.
- Implementation: são as classes que implementam a Interface, ou seja, são as implementações concretas.

— Você consegue perceber cada um dos itens citados acima na figura abaixo?

http://pages.cpsc.ucalgary.ca/~eberly/Courses/CPSC333/Lectures/Design/patterns.html

Problema

Vamos criar um programa que vai atuar como um editor de textos, onde podemos digitar nosso texto, dar nome ao nosso arquivo e salvar para PDF e/ou TXT.

Para isso, criamos a classe AwesomeTextEditor, onde iremos dispor de alguns métodos como:

  • setText: nos permitirá atribuir um texto
  • getText: nos permitirá obter o texto que digitamos
  • setDocumentName: nos permitirá atribuir um nome para nosso documento
  • getDocumentName: nos permitirá obter o nome do nosso documento
  • exportText: nos permitirá exportar nosso texto para PDF e/ou TXT.

criei dois métodos privados para evitar inchar o nosso Switch, que é um dos problemas típicos que clamam por usar o Strategy Pattern. Um ótimo caso onde requer que extraiamos a lógica para uma classe externa para evitar a bagunça.

O exemplo abaixo até funciona, mas não está tão elegante e tenderemos a inchar nossa classe caso queiramos adicionar mais habilidades de exportar para outros formatos, sem contar que poderemos deixar nosso código bem confuso e gigante. Podemos desacoplar boa parte da nossa lógica e concentrar em classes isoladas onde teremos mais domínio caso queiramos fazer modificações bem pontuais.

exemplo de código que podemos aplicar o Strategy Pattern.

Solução

Vamos usar o Strategy para resolver o problema do acoplamento da lógica relacionada a conversão do nosso texto para PDF e/ou TXT.

O primeiro passo é entendermos o que precisamos extrair. Já sabemos que é conversão de texto para os formatos disponíveis.
Tendo isso em mente, precisamos criar um diagrama para termos uma visão do que iremos fazer.

diagrama do core do nosso editor de textos.

Com o diagrama em mãos, partimos para a implementação.

Strategy abstrato: aqui iremos definir o contrato para as classes implementarem nosso Strategy.
Observamos que ambos, PDF e TXT, irão precisar do documentName e do text, então, colocamos isso no nosso contrato e também o método para exportar.

FileCreatorInterface

Strategy concreto: aqui é onde iremos implementar a lógica relacionada ao que queremos fazer, no caso, exportar para PDF.

Implementação do Strategy para o PDF

Implementação do Strategy para o TXT

Implementação do Context: o context fará uso do Strategy disponível. Quem precisar utilizar as habilidades de conversão para PDF ou TXT deverá chamar o Context. Poderemos ter um método de update afim de atualizar o nosso Strategy. Atualmente, dispomos de 2: PDFCreator e TXTCreator.

Voltando a nossa classe AwesomeTextEditor teremos o seguinte trecho de código. Iremos fazer uso do contexto e passar uma Strategy para ele.
Quando o usuário selecionar um tipo para exportar, invocaremos o nosso método update que está disponível no context, afim de atualizar a Strategy que precisaremos.

E finalmente, no nosso main, precisaremos fazer apenas isso:

Conclusão

O lado bom de usar o Strategy é que podemos isolar nosso código, a ponto de simplificar nosso código e diminuir significativamente a quantidade de código que teremos. Sempre que precisarmos fazer alguma modificação, iremos direto na implementação concreta do nosso Strategy e evitamos um caos. Quem tiver que mexer no código, vai diretamente no arquivo necessário e estará tudo certo. Ganhamos também testabilidade, uma vez que teremos trechos de códigos isolados, irá facilitar que criemos testes unitários.

Ao meu ver, o ponto negativo foi o fato de termos que criar várias implementações concretas. Imagina um contexto em que podemos exportar nosso texto para vários formatos diferentes… imagina a quantidade de classes que precisaremos criar… minha cabeça chega bagunçou um pouco.
Outro ponto que também não gostei muito foi o de termos que criar vários outros arquivos: Interface, Implementações concretas, contexto… tudo isso diminui um pouco o tempo de desenvolvimento, porém, te dá o ganho de ter as coisas separadas e quanto precisar de uma eventual manutenção, você não vai sofrer em uma classe inchada de código e sair procurando feito louco e correr o risco de quebrar tudo, você tem trechos de código isolados.

--

--

Alcides Junior

iOS Software Engineer @ iFood —  Developer Academy 2018/19 — WWDC Scholarship 2019, 20 and 21 — Graduando em Engenharia de Computação IFCE