Padrões de Projeto: entenda o que são e quais seus benefícios

Fala Dev, tudo em riba?

Hoje vamos mergulhar em assunto que não é dos mais emocionantes, porém se você sabe que existe e como utilizar, será disparado um engenheiro de software melhor do que a média do mercado. O assunto de hoje é Padrões de Projetos (Design Patterns).

Vamos entender o que são esses tais padrões de projetos, benefícios, classificações existentes e quando utilizar.

Um pouco de contexto

Acredito que todo desenvolvedor já passou por algo semelhante. Todo projeto de software, geralmente inicia pequeno, com estrutura simples e a medida que o tempo vai passando, novas features vão sendo adicionadas e o software começa a ganhar corpo.

No entanto, pode chegar em um ponto em que a cada nova funcionalidade a ser adicionada, aumenta o tempo para a completa implementação assim como a complexidade para implementar tal funcionalidade, por mais simples que seja. O foco passa a ser na entrega em detrimento da qualidade e isso cobra seu preço ao executar novas implementações ou correções de funcionalidades existentes.

Nesse ponto, nos deparamos com classes inchadas que ao invés de ter uma única responsabilidade, tem várias e cada uma totalmente diferente das demais. Surgem if’s encadeados que tornam ainda mais complexo o entendimento da lógica ou regra implementada aumentando a carga cognitiva necessária para entender o código, sem falar no volume de código duplicado espalhado pelo software. É nesse momento que bate aquele frio na barriga ao tentar trabalhar no código.

Tudo isso é um claro efeito da codificação desenfreada sem antes analisar ou arquitetar o como implementar cada nova feature e se existem soluções (ou melhor dizendo, padrões) que podem ser aplicados para que o software cresça de forma organizada e saudável, mantendo sua performance e fácil manutenibilidade.

O que é um Padrão de Projeto?

Padrões de Projetos (Design Patterns) são soluções testadas (bem testadas) e que podem ser reutilizadas para resolver problemas comuns em projetos de software orientados a objetos. Não é um pedaço de código que você copia e cola em seu projeto, é um conceito, uma ideia ou solução geral que pode ser utilizada em nosso dia a dia de desenvolvimento de software para resolver os mais diversos problemas.

A primeiro momento podemos confundir um padrão de projeto com um algoritmo, porém ambos são coisas distintas. Um algoritmo nada mais é do que uma sequência de passos que são executados para resolver um problema ou um parte dele.

Já o padrão de projeto é a forma como os algoritmos podem ser estruturados e organizados de modo a resolver um problema por completo.

Para um melhor entendimento, vamos utilizar como analogia a construção de uma casa. Na Engenharia Civil, existem padrões arquitetônicos testados que são utilizados durante o processo de construção.

Toda casa precisa ter meios de entrar, sair, paredes resistentes, portas, janelas, telhado, etc. O engenheiro ao especificar no projeto do imóvel que a porta de acesso a um banheiro terá 80 centímetros de largura por 210 centímetros de altura não está inventando essas medidas ou adicionando a esmo tais medidas ao projeto. É um padrão que foi testado e comprovado que em alguns casos, faz sentido ter no banheiro uma porta com essa medida para facilitar a acessibilidade de pessoas nas mais diversas condições.

No contexto de desenvolvimento de software é a mesma coisa, para resolver determinado problema, pode existir um padrão que foi testado e se encaixa perfeitamente naquele contexto.

Isso não significa que seu software vai ser limitado ou engessado em termos de funcionalidades devido a aplicação de padrões de projeto, pelo contrário. Os padrões de projetos são flexíveis e permitem que você os adapte à realidade do seu software, mantendo suas particularidades.

É totalmente possível trabalhar como programador sem saber um padrão de projeto sequer. E mais, as vezes acidentalmente você pode criar uma estrutura de código para resolver um problema e, sem se dar conta, pode estar utilizando algum padrão de projeto.

No entanto, ter conhecimento padrões de projeto pode trazer muitos benefícios no desenvolvimento e ao longo do ciclo de vida de um software. Alguns deles são:

  • Comunicação eficaz entre integrantes do time, visto que o padrão de projeto utiliza uma linguagem comum para a resolução de determinados problemas de software;
  • Facilidade no entendimento e manutenção do código, visto que é um padrão conhecido pelos devs;
  • Desenvolvimento acelerado da solução, visto que não é gasto tempo criando uma forma de resolver o problema que pode ser que dê certo;
  • Código robusto e flexível, visto que os padrões tendem a seguir os princípios, por exemplo, do S.O.L.I.D.;
  • Permite a reutilização de código de forma mais eficiente;

Classificações

Os padrões de projetos de uso universal são classificados de acordo com seu propósito ou intenção. Geralmente, ao pesquisarmos ou estudarmos os padrões, vamos nos deparar com algumas seções que estão presentes em sua descrição para entendermos seu conceito e quando podemos aplicar. Em geral as seções são:

  • Propósito: descreve brevemente o problema e a solução;
  • Motivação: aprofunda na explicação do problema e a solução que o padrão apresenta;
  • Estruturas: classes que demonstram como as partes que compõe o padrão se relacionam;
  • Exemplos de código: exemplos de código demonstrando a aplicação do padrão.

Dito isso, podemos encontrar as seguintes classificações de padrões de projeto:

  • Criacionais;
  • Estruturais;
  • Comportamentais.

Esses padrões foram catalogados no famoso livro “Design Patterns: Elements of Reusable Object-Oriented Software” (1994), escrito pela “Gang of Four” (GoF): Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides. Este livro se tornou a referência fundamental sobre o assunto e documentou 23 padrões clássicos de projetos.

Padrões Criacionais

Esses padrões cuidam de como os objetos nascem, de que forma eles são construídos. A forma que isso é feito é genial, pois a ideia principal é que os objetos sejam criados de uma forma que o restante do código não precisa saber como foram criados e tão pouco se preocupar com isso, trazendo maior segurança e flexibilidade na criação e gestão de tais objetos.

Com isso, a criação de tais objetos fica totalmente desacoplada do restante do software, o que permite qualquer tipo de alteração futura sem que o software quebre por completo.

Alguns exemplos de padrões criacionais são:

  • Singleton: garante que uma classe tenha apenas uma instância em toda a aplicação;
  • Factory Method: define uma interface para criar objetos, permitindo que subclasses decidam qual classe instanciar;
  • Builder: permite construir objetos complexos passo a passo.

Vamos a um exemplo: em determinado fluxo do software é necessário montar uma consulta SQL que retorna uma lista de itens. Essa consulta pode receber vários parâmetros que são utilizados para filtrar os itens a serem retornados. Podemos nesse caso utilizar o padrão Builder que possibilita a construção dessa query SQL de forma simplificada, sem a necessidade de ir adicionar if’s e mais if’s para testar se um parâmetro foi fornecido para adicioná-lo a query. Em artigos futuros abordaremos esses padrões de forma mais detalhada.

Padrões Estruturais

Tendo os objetos em mãos, precisamos agrupá-los de forma organizada para resolver um problema ou adicionar uma funcionalidade no software. É ai que entram os padrões estruturais, que garantem a organização dos objetos de forma harmoniosa, porém não deixando de lado a flexibilidade. Se algum objeto for alterado, a estrutura como um todo permanece funcional.

Alguns exemplos de padrões estruturais são:

  • Adapter: permite que interfaces incompatíveis trabalhem juntas;
  • Decorator: adiciona responsabilidades a objetos dinamicamente;
  • Composite: permite tratar objetos individuais e composições de objetos de maneira uniforme.

Vamos a um exemplo: pensem em um gerenciador de arquivos, ele pode lidar com um objeto simples, como por exemplo um arquivo ou com um objeto mais complexo, como por exemplo uma pasta que contém vários arquivos. Você pode criar uma função buscar que utiliza o padrão Composite que pode buscar arquivos, sem se preocupar se esse arquivo está fora ou dentro de pastas.

Padrões Comportamentais

São padrões que determinam como os objetos se comunicam entre eles, como colaboram e interagem no dia a dia. O foco desses padrões são os algoritmos e como as responsabilidades são distribuídas entre os objetos. Esses padrões visam deixar as comunicações entre eles mais eficiente, clara e flexível.

Alguns exemplos de padrões comportamentais são:

  • Strategy: define uma família de algoritmos e os torna intercambiáveis;
  • Observer: define uma dependência um-para-muitos entre objetos, permitindo notificações automáticas;
  • Command: encapsula uma requisição como um objeto, permitindo parametrizar clientes com diferentes requisições.

Vamos a um exemplo: pense em uma loja virtual que oferece os meios de pagamentos boleto e cartão de crédito. Podemos utilizar o padrão Strategy que, da forma que ele é estruturado, identifica a forma de pagamento escolhida e direciona o processamento do pagamento para o algoritmo específico. Se amanhã for necessário disponibilizar uma nova forma de pagamento, como por exemplo o Pix, basta adicionar o algoritmo específico para essa forma de pagamento e adicionar sua referência ao código responsável por orquestrar qual forma de pagamento está vindo para fazer o correto redirecionamento, sem a necessidade de alterar o código das demais formas de pagamento.

Quando NÃO usar padrões de projetos?

Embora os padrões de projetos sejam poderosos, é importante reconhecer quando eles não são necessários:

  • Projetos pequenos e simples: aplicações com poucas funcionalidades e sem perspectiva de crescimento não justificam a complexidade adicional;
  • Over-engineering: adicionar padrões “por precaução” pode criar complexidade desnecessária;
  • Princípio YAGNI (You Aren’t Gonna Need It): não antecipe problemas que ainda não existem. Aplique padrões quando o problema real aparecer;
  • Time inexperiente: em times sem conhecimento prévio, pode ser mais produtivo começar simples e refatorar depois.

Lembre-se: é melhor ter código simples que funciona do que código complexo e “elegante” que ninguém entende.

Próximos Passos

Se você quer se aprofundar em padrões de projetos, aqui vão algumas recomendações:

Livros:

Por onde começar:

Recomendo começar pelos padrões mais utilizados no dia a dia:

  1. Strategy – para lidar com diferentes algoritmos
  2. Factory Method – para criação de objetos
  3. Singleton – para instâncias únicas (use com cuidado!)

Próximos artigos:

Nos próximos artigos desta série, vamos mergulhar em cada padrão individualmente, com exemplos práticos de código e casos de uso reais. Fique ligado!

Conclusão

No fim das contas, podemos notar que usar padrões é muito mais do que só uma técnica de código, é sobre ter a sabedoria de utilizar as ferramentas certas. É estar aberto a uma mudança de mentalidade, um jeito diferente de pensar e resolver problemas de software.

É claro que não existe bala de prata. Você não vai sair utilizando padrões de projeto para tudo. Para softwares muito simples que dificilmente haverá uma evolução ou crescimento, utilizar padrões de projetos seria um exagero. É como se você tentasse matar uma formiga com um míssil intercontinental. Por outro lado, em softwares grandes e complexos, os benefícios em utilizar padrões de projetos são enormes, superam e muito o esforço inicial em aplicar um padrão ou vários padrões de projetos.

O mais importante é entender o conceito! O valor não está em decorar a estrutura de cada padrão. A verdadeira sabedoria está em entender de verdade os problemas que aquele padrão resolve. Quando a gente entende o porquê, implementar é só um detalhe.

Agora é com você! Escolha um projeto pessoal e tente identificar onde um padrão de projeto poderia ser aplicado. A melhor forma de aprender é praticando.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *