Coesão e acoplamento: as métricas fundamentais
1. Introdução: O que são Coesão e Acoplamento?
Coesão e acoplamento são duas métricas essenciais para avaliar a qualidade de um projeto de software. Elas medem, respectivamente, o quão bem organizadas estão as responsabilidades dentro de um módulo e o quão dependentes os módulos são entre si.
Coesão refere-se ao grau em que os elementos de um módulo (funções, métodos, classes) pertencem logicamente uns aos outros. Um módulo com alta coesão tem um propósito único e bem definido, enquanto um módulo com baixa coesão agrupa funcionalidades não relacionadas.
Acoplamento mede a força das conexões entre módulos. Baixo acoplamento significa que os módulos são independentes e podem ser alterados sem impacto significativo em outros. Alto acoplamento cria dependências rígidas, tornando o sistema frágil e difícil de modificar.
Essas métricas são fundamentais porque impactam diretamente a manutenibilidade, testabilidade e escalabilidade do software. Um sistema com alta coesão e baixo acoplamento é mais fácil de entender, modificar e testar.
2. Coesão: Tipos e Níveis
A coesão pode ser classificada em uma escala do melhor para o pior:
- Coesão funcional (ideal): Todos os elementos contribuem para uma única função bem definida.
- Coesão sequencial: A saída de uma parte serve como entrada para outra.
- Coesão comunicacional: Elementos operam sobre os mesmos dados.
- Coesão procedural: Elementos são agrupados porque são executados em sequência.
- Coesão temporal: Elementos são agrupados porque ocorrem no mesmo momento.
- Coesão lógica: Elementos realizam funções similares, mas não relacionadas.
- Coesão coincidental (pior): Elementos são agrupados aleatoriamente, sem relação.
Exemplo de código
Baixa coesão (coesão coincidental):
class Utilitarios {
fun calcularImposto(valor: Double): Double { ... }
fun enviarEmail(destinatario: String, mensagem: String) { ... }
fun gerarRelatorioPDF(dados: List<Dados>) { ... }
fun validarCPF(cpf: String): Boolean { ... }
}
Alta coesão (coesão funcional):
class CalculadoraImposto {
fun calcularImpostoRenda(valor: Double): Double { ... }
fun calcularICMS(valor: Double): Double { ... }
fun calcularISS(valor: Double): Double { ... }
}
class ServicoEmail {
fun enviarEmail(destinatario: String, mensagem: String) { ... }
fun validarDestinatario(email: String): Boolean { ... }
}
3. Acoplamento: Tipos e Impactos
O acoplamento também possui uma escala do pior ao melhor:
- Acoplamento de conteúdo (pior): Um módulo modifica dados internos de outro.
- Acoplamento comum: Módulos compartilham dados globais.
- Acoplamento de controle: Um módulo controla o fluxo de outro.
- Acoplamento de dados (melhor): Módulos trocam dados simples por parâmetros.
Exemplo: dependência direta vs. injeção de dependência
Alto acoplamento (dependência direta):
class PedidoService {
private val repositorio = PedidoRepositorio() // instância direta
fun salvar(pedido: Pedido) {
repositorio.inserir(pedido)
}
}
Baixo acoplamento (injeção de dependência):
class PedidoService(private val repositorio: PedidoRepositorio) {
fun salvar(pedido: Pedido) {
repositorio.inserir(pedido)
}
}
// Uso:
val repositorio = PedidoRepositorio()
val service = PedidoService(repositorio)
4. Relação entre Coesão e Acoplamento
Existe uma relação inversa natural: alta coesão geralmente leva a baixo acoplamento. Quando um módulo tem um propósito único e bem definido, ele naturalmente depende menos de outros módulos para realizar suas tarefas.
Refatoração de um sistema monolítico
Antes (baixa coesão e alto acoplamento):
class SistemaFinanceiro {
fun processarPedido(pedido: Pedido) {
// Valida estoque
// Calcula imposto
// Processa pagamento
// Envia email
// Gera nota fiscal
}
}
Depois (alta coesão e baixo acoplamento):
class ProcessadorPedido(
private val validadorEstoque: ValidadorEstoque,
private val calculadoraImposto: CalculadoraImposto,
private val processadorPagamento: ProcessadorPagamento,
private val servicoEmail: ServicoEmail,
private val geradorNotaFiscal: GeradorNotaFiscal
) {
fun processar(pedido: Pedido) {
validadorEstoque.validar(pedido)
val imposto = calculadoraImposto.calcular(pedido)
processadorPagamento.processar(pedido.total)
servicoEmail.enviarConfirmacao(pedido)
geradorNotaFiscal.gerar(pedido, imposto)
}
}
5. Medindo Coesão e Acoplamento na Prática
Métricas quantitativas
LCOM (Lack of Cohesion of Methods): Mede quantos pares de métodos não compartilham atributos comuns. Valores altos indicam baixa coesão.
CBO (Coupling Between Objects): Conta o número de classes externas que uma classe depende. Valores altos indicam alto acoplamento.
Ferramentas de análise estática
- SonarQube: Fornece métricas de coesão e acoplamento
- JDepend (Java): Calcula CBO, LCOM e outras métricas
- NDepend (.NET): Análise avançada de dependências
- PyMetrics (Python): Métricas para código Python
Interpretação dos resultados
- LCOM < 0.5: Alta coesão (aceitável)
- LCOM > 0.8: Baixa coesão (alarmante)
- CBO < 5: Baixo acoplamento (ideal)
- CBO > 15: Alto acoplamento (preocupante)
6. Estratégias para Melhorar Coesão e Reduzir Acoplamento
Aplicação de princípios SOLID
- Single Responsibility Principle (SRP): Cada classe deve ter apenas uma razão para mudar
- Dependency Inversion Principle (DIP): Dependa de abstrações, não de implementações concretas
Uso de interfaces e abstrações
// Definição da abstração
interface RepositorioPedido {
fun salvar(pedido: Pedido): Long
fun buscarPorId(id: Long): Pedido?
}
// Implementação concreta
class RepositorioPedidoBanco : RepositorioPedido {
override fun salvar(pedido: Pedido): Long { ... }
override fun buscarPorId(id: Long): Pedido? { ... }
}
// Uso desacoplado
class PedidoService(private val repositorio: RepositorioPedido) { ... }
Refatoração de classes "faz-tudo"
Identifique classes que fazem muitas coisas diferentes e divida-as em módulos coesos. Use o padrão Facade para simplificar interfaces complexas.
7. Relação com Outros Princípios e Padrões
Design Patterns
- Strategy: Permite variar algoritmos sem acoplamento rígido
- Observer: Desacopla objetos que precisam ser notificados
- Factory Method: Desacopla a criação de objetos
- Dependency Injection: Reduz acoplamento ao injetar dependências
DRY, KISS e YAGNI
- DRY (Don't Repeat Yourself): Código duplicado reduz coesão
- KISS (Keep It Simple, Stupid): Simplicidade favorece alta coesão
- YAGNI (You Ain't Gonna Need It): Evite acoplamento desnecessário
Lei de Demeter
"Fale apenas com seus amigos imediatos." Isso reduz acoplamento ao evitar encadeamento excessivo de chamadas:
// Ruim (viola Lei de Demeter)
pedido.cliente.endereco.cidade
// Melhor
pedido.obterCidadeCliente()
8. Conclusão e Boas Práticas
Coesão alta + acoplamento baixo = código sustentável. Essa combinação produz sistemas que são:
- Fáceis de entender: Cada módulo tem um propósito claro
- Fáceis de modificar: Alterações têm impacto localizado
- Fáceis de testar: Módulos independentes são mais testáveis
- Fáceis de reutilizar: Módulos coesos são mais portáteis
Checklist para revisão de arquitetura
- [ ] Cada classe tem uma única responsabilidade?
- [ ] As dependências são injetadas, não instanciadas?
- [ ] Métodos que não usam atributos da classe foram movidos?
- [ ] As interfaces são pequenas e focadas (Interface Segregation)?
- [ ] O código viola a Lei de Demeter?
Dicas para times
- Revise o acoplamento em code reviews — pergunte "essa dependência é necessária?"
- Use análise estática contínua — configure ferramentas como SonarQube
- Refatore pequenas partes — não tente resolver tudo de uma vez
- Documente dependências — mantenha um mapa de dependências do sistema
- Eduque o time — compartilhe conhecimento sobre boas práticas
Lembre-se: coesão e acoplamento não são absolutos. O objetivo não é eliminá-los completamente, mas encontrar o equilíbrio certo para o contexto do seu sistema.
Referências
- Martin Fowler - Cohesion — Definição detalhada de coesão por Martin Fowler, com exemplos práticos
- Robert C. Martin - The Principles of OOD — Artigo clássico sobre princípios SOLID e sua relação com coesão e acoplamento
- SonarQube Documentation - Metrics — Documentação oficial com definições de métricas como LCOM e CBO
- Microsoft Docs - Coupling and Cohesion — Artigo técnico da MSDN Magazine sobre padrões de coesão e acoplamento
- Refactoring Guru - Design Patterns — Catálogo de padrões de projeto com exemplos que ilustram redução de acoplamento
- NDepend - Coupling Metrics — Guia prático sobre métricas de acoplamento para análise de código .NET
- IEEE - A Metrics Suite for Object Oriented Design — Artigo acadêmico seminal sobre métricas LCOM e CBO (Chidamber & Kemerer)