Coesão e Acoplamento

Hello World 😁 💻

No artigo de hoje iremos falar sobre dois conceitos fundamentais na Engenharia de Software, usados para criar sistemas organizados, fáceis de entender, manter e evoluir. Estamos falando de Coesão e Acoplamento.

A ideia central

A melhor maneira de entender os dois é pensar que estamos avaliando duas coisas diferentes:

  • Coesão: o quanto as responsabilidades dentro de um módulo estão relacionadas entre si.
  • Acoplamento: o quanto um módulo depende de outros módulos para funcionar.

Vamos entender por partes como cada um desses conceitos funcionam.

O que é coesão?

Coesão diz respeito à organização interna de uma classe ou módulo. Ele mede o nível de foco das responsabilidades dentro de uma única classe, módulo ou função. Uma classe altamente coesa faz apenas uma coisa, mas a faz excepcionalmente bem (conceito diretamente ligado ao Princípio da Responsabilidade Única do SOLID).

Uma classe tem alta coesão quando suas responsabilidades estão diretamente relacionadas a um único propósito bem definido.

Nisso temos duas formas de medir a coesão de um código:

  • Alta Coesão: Os métodos e propriedades da classe trabalham juntos para realizar um único objetivo lógico. Uma classe CalculadoraDeImpostosService que possui exclusivamente os cálculos fiscais, é uma classe com alta coesão.

  • Baixa Coesão: Acontece quando uma classe acumula responsabilidades desconexas (a famosa God Class). Se você tem um serviço que gerencia transações no banco de dados, valida regras de negócio de login, serializa dados JSON para o front-end e ainda orquestra o envio de e-mails, ele tem baixíssima coesão. Alterar o layout do e-mail pode acidentalmente gerar um bug no fluxo de banco de dados.

Exemplo prático:

java
// ❌ Baixa coesão - múltiplas responsabilidades class Usuario { void criarUsuario() {} void enviarEmail() {} void gerarRelatorio() {} void processarPagamento() {} } // ✅ Alta coesão - responsabilidade focada class Usuario { void criar() {} void atualizar() {} void deletar() {} Usuario obter() { return null; } } // Responsabilidades delegadas a outras classes class EmailService { void enviar() {} } class RelatorioService { void gerar() {} } class PagamentoService { void processar() {} }

Em resumo:

Baixa coesão (evitar):

  • Elementos desconexos dentro da mesma classe;
  • Múltiplas responsabilidades;
  • Difícil entender o propósito.

Alta coesão (ideal):

  • Elementos trabalham juntos para um propósito comum;
  • Responsabilidades bem definidas (Single Responsibility);
  • Código mais organizado e compreensível.

O que é acoplamento?

Acoplamento diz respeito às dependências entre classes ou módulos. Ele define o quanto uma parte do sistema "sabe" sobre o funcionamento interno e os detalhes de implementação da outra.

Um módulo tem alto acoplamento quando depende muito de detalhes de implementação de outros módulos, tornando as mudanças em uma parte capazes de afetar várias outras.

Nisso temos duas formas de medir o acoplamento de um código:

  • Alto Acoplamento (Tight Coupling): As classes estão rigidamente conectadas. Se um componente precisa mudar, você é obrigado a alterar diversas outras partes do código. Instanciar dependências diretamente com um new ServicoEmailConcreto() dentro do construtor da sua classe cria alto acoplamento, pois sua classe fica engessada àquela implementação específica.

  • Baixo Acoplamento (Loose Coupling): Os módulos interagem através de contratos claros (interfaces), mas de forma independente.

Exemplo prático:

java
// ❌ Alto acoplamento - a classe depende de implementação específica class Usuario { private PostgreSQL bancoDados; public Usuario() { this.bancoDados = new PostgreSQL(); // Acoplado a PostgreSQL } } // ✅ Baixo acoplamento - depende de abstração interface BancoDados { void salvar(Usuario usuario); } class Usuario { private BancoDados bancoDados; public Usuario(BancoDados bancoDados) { this.bancoDados = bancoDados; // Pode ser qualquer BD que implemente a interface } }

No ecossistema Spring Boot e no Angular, o baixo acoplamento é o motivo principal pelo qual o padrão de Injeção de Dependências (DI) é a base das arquiteturas. Em vez de um componente instanciar um serviço de dados por conta própria, ele simplesmente declara a dependência em seu construtor, e o framework gerencia a instanciação. Isso garante que o estado do objeto permaneça válido e permite que você substitua facilmente aquele serviço em testes unitários sem tocar no código do componente.

Como os dois conceitos trabalham juntos?

Podemos combinar os dois conceitos de quatro maneiras:

O objetivo não é simplesmente dividir o sistema em muitas classes. É distribuir as responsabilidades de maneira coerente e estabelecer dependências que possam ser compreendidas e modificadas sem grandes efeitos colaterais.

Exemplo Ilustrativo

Imagine uma equipe de trabalho. Nesse cenário os conceitos se aplicam da seguinte forma:

  • Alta coesão: cada equipe tem uma missão clara, e suas atividades contribuem para essa missão.
  • Baixo acoplamento: uma equipe consegue cumprir sua missão sem precisar conhecer todos os detalhes internos das demais.

Em resumo, Coesão é sobre fazer uma coisa bem; Acoplamento é sobre depender do mínimo possível, e do que é estável. O bom design maximiza a primeira e torna a segunda explícita, intencional e na direção certa.

Conclusão

A busca pelo equilíbrio ideal entre coesão e acoplamento é o que define a maturidade e a longevidade de um projeto de software. O objetivo arquitetural deve ser sempre alcançar a alta coesão, garantindo que cada módulo seja um especialista focado em uma única responsabilidade, operando em harmonia com o baixo acoplamento, onde esses componentes interagem por meio de fronteiras claras e contratos (interfaces), sem depender dos detalhes internos uns dos outros.

Sistemas desenhados com essa mentalidade tornam-se vivos e adaptáveis. Quando um novo requisito de negócio surge, um bug é descoberto ou uma ferramenta de terceiros precisa ser substituída, uma arquitetura bem ajustada permite que a equipe faça alterações cirúrgicas. Em vez de temer efeitos colaterais imprevisíveis e "quebras" em cascata por todo o sistema, o desenvolvedor ganha a confiança de que a base de código é modular, isolada e fácil de ser testada.

No fim das contas, dominar a balança entre coesão e acoplamento é a diferença fundamental entre escrever um "código espaguete" frágil, que apenas funciona provisoriamente, e projetar uma solução de engenharia robusta, capaz de escalar e evoluir de forma sustentável a longo prazo.

Se você chegou até aqui, espero que tenha gostado do artigo.

Obrigada pela atenção e até breve.😁

Laryssa Ramos

Voltar para os artigos