Como começar com Design Patterns
Quando estamos aprendendo a programar, é comum nos preocuparmos apenas em fazer o software funcionar — e essa já é uma parte bem importante. Porém, ao caminharmos na nossa jornada como desenvolvedoras de software, começamos a pensar em outros aspectos acerca de como podemos melhorar a qualidade do que desenvolvemos. Se estamos trabalhando em um projeto com mais pessoas, por exemplo, como podemos tornar esse código mais legível, fácil de entender, dar manutenção e criar nossas funcionalidades em cima do que já existe? Como podemos evitar a duplicação do código? Como tornar o código mais flexível?
Existem alguns termos que ouvimos com frequência que buscam solucionar essas questões, como as famosas Design Patterns (Padrões de Projeto). Mas afinal, o que são Design Patterns? Podemos começar falando sobre o que elas não são.
Design Patterns não são fórmulas mágicas que solucionam problemas, não são códigos prontos que inserimos nas nossas aplicações e também não são algoritmos. Design Patterns, como o nome diz, são apenas padrões que oferecem um conceito de uma solução para um problema de design que acontece com frequência. A implementação de cada padrão pode variar muito de acordo com o domínio, a linguagem utilizada e outros fatores. Esses padrões foram criados a partir da experiência de pessoas desenvolvedoras, que foram experimentando soluções diferentes para problemas recorrentes que encontraram ao longo dos anos. Um livro chamado Padrões de Projeto — Soluções Reutilizáveis de Software Orientado a Objetos foi publicado há um bom tempo atrás, apresentando 23 Padrões de Projeto, e, desde então, muitos outros padrões já foram identificados. Esses padrões até mesmo influenciaram linguagens de programação e, como tudo na área de engenharia de software, estão em constante mudança.
Por que aprender Design Patterns?
Imagine que você está fazendo algo novo pela primeira vez. Por exemplo, aprendendo uma receita nova. Você sabe em qual resultado quer chegar e tem uma ideia de como misturar os ingredientes para isso. Porém, ajudaria muito se você pudesse consultar uma receita que te guiasse para chegar nesse resultado de uma forma que muitas pessoas já fizeram antes, evitando muitos problemas que você poderia encontrar ao longo do caminho. É mais ou menos assim que funciona com os padrões de projeto.
Muitas pessoas desenvolvedoras já testaram esses padrões, enfrentaram esses problemas e documentaram essas soluções. Além disso, Design Patterns definem uma linguagem única para que possamos conversar sobre soluções possíveis com nosso time, até mesmo durante o pareamento.
É a mesma coisa que princípios SOLID?
Não, mas tem tudo a ver. Na verdade, acredito ser necessário aprofundar conhecimentos nos princípios SOLID antes de começar a estudar Design Patterns, até porque esses princípios são utilizados em muitos dos padrões. O objetivo é nos ajudar a criar software de fácil manutenção e entendimento. O nome SOLID é um acrônimo para:
- Single Responsibility: uma classe deve ter uma única responsabilidade.
- Open/Closed: as classes devem estar fechadas para mudanças e abertas para extensão. Ou seja, se uma classe funciona direitinho e já foi testada, deve ser evitado alterar ela. Para inserir um novo comportamento, devemos extender a classe ao invés de modificá-la.
- Liskov Substitution: uma subclasse deve poder substituir a superclasse sem alterar o comportamento do sistema.
- Interface Segregation: uma classe não deve depender de métodos que não usa.
- Dependency Inversion: as dependências podem determinar o quão fácil é fazer mudanças no sistema. Se tivermos muitas, isso pode levar a alto acoplamento. Esse princípio diz que módulos de alto nível devem depender de generalizações de alto nível, e não de detalhes.
Os padrões de projeto seguem alguns destes princípios de design para cobrir assuntos como flexibilidade e manutenção. Por exemplo, muitas Design Patterns utilizam o princípio Open/Closed, seguindo a ideia de que deve ser possível extender seu software, ou então o princípio de Dependency Inversion, generalizando comportamentos através de interfaces. Por isso, faz sentido entender bem esses princípios de design para então seguir para as Design Patterns.
Que tipos de Design Patterns existem?
Os padrões de projeto foram classificados em três tipos diferentes, com base no seu propósito.
Os padrões de criação, ou Creational Patterns, são sobre o processo de criar objetos. Dependendo do contexto ou do nosso objetivo, a forma tradicional de criação pode não ser a melhor solução. Por exemplo, se tivermos uma classe para conexão com banco de dados, podemos querer ter apenas uma instância da classe, ao invés de ter novas conexões em diversos lugares do sistema. Para isso, poderíamos usar o padrão chamado Singleton. Existe ainda outros padrões que se encaixam no tipo Creational Patterns, como o Factory Method, Builder, Prototype, entre outros, todos buscando solucionar problemas relacionados a criação de objetos.
Os padrões estruturais, ou Structural Patterns, envolvem como os objetos se conectam e se relacionam. Esses padrões são necessários porque quanto mais um sistema cresce, mas complexo tende a ficar. Com mais complexidade, precisamos encontrar uma maneira sustentável de fazer os objetos se relacionarem e que permita a criação de novas funcionalidades de forma mais simples. Por exemplo, digamos que criamos um hambúrguer e queremos adicionar itens dinamicamente, como queijo extra ou salada. Ao invés de criarmos uma classe hambúrguer e diversas subclasses com cada tipo de item adicional, poderíamos aplicar o padrão Decorator, que utiliza agregação para combinar comportamentos dinamicamente. Outros padrões conhecidos deste tipo incluem Adapter, Composite e Facade.
Os padrões de comportamento, ou Behavioural Patterns, lidam com a forma que os objetos distribuem o trabalho, como colaboram para atingir um objetivo comum. Se voltarmos para o caso do hambúrguer, e quisermos avisar nossos clientes sempre que um hambúrguer ficar pronto, poderíamos utilizar um padrão desse tipo, o Observer, no qual teríamos clientes observando e esperando serem notificados. Esse tipo de padrão inclui outros muito utilizados, como Command, Iterator e Strategy.
Como saber quais padrões utilizar?
Há um catálogo grande de Design Patterns e dificilmente vamos decorar todos depois de ler um livro sobre o assunto. O importante é, primeiramente, saber que esses padrões existem. Depois, entender qual problema estamos tentando resolver e, então, nos aprofundarmos nos padrões que buscam solucionar esses problemas. Entender melhor como o padrão é aplicado e olhar códigos de exemplo é um bom ponto de partida. É importante lembrar que não existe uma "resposta certa" quando se trata de Design Patterns, é uma escolha que deve ser tomada com base no contexto, nas necessidades e avaliando o trade-off.
Esse artigo foi baseado em minhas anotações ao estudar Design Patterns, com o intuito de que possa auxiliar quem está começando a estudar o tema agora.
Referências e Materiais de Estudo
- Livro: Padrões de Projetos: Soluções Reutilizáveis de Software Orientados a Objetos
- https://sourcemaking.com/design_patterns
- https://refactoring.guru/design-patterns
- https://www.coursera.org/learn/design-patterns/
- https://www.baeldung.com/solid-principles