Introdução
O nome do princípio vem do próprio nome do projeto de pesquisa (que remete a Deméter, deusa grega da agricultura — a metáfora era "cultivar" software que cresce de forma incremental e adaptável, não do princípio de acoplamento em si).
O projeto Demeter investigava como reduzir o custo de manutenção de sistemas orientados a objetos observando que boa parte das mudanças de software quebrava código muito distante do ponto onde a mudança real acontecia — um efeito cascata causado por classes que conheciam profundamente a estrutura interna de outras classes.
Essa observação foi confirmada empiricamente alguns anos depois: em 1994, Chidamber & Kemerer publicaram as famosas métricas CK (A Metrics Suite for Object Oriented Design), nas quais o CBO (Coupling Between Objects) — quão acoplada uma classe é a outras — se tornou um dos preditores mais fortes de defeitos e esforço de manutenção em estudos empíricos posteriores de engenharia de software. Ou seja: a intuição por trás da Law of Demeter (menos acoplamento = menos bugs ao mudar código) tem respaldo em dados de décadas de pesquisa empírica em qualidade de software.
Definição
Também chamada de "Principle of Least Knowledge", a formulação clássica é:
Um método
Mde um objetoOsó deve chamar métodos de:
- O próprio
O- Os parâmetros recebidos por
M- Qualquer objeto que
Mcrie/instancie internamente- Os componentes diretos de
O(seus atributos/campos)- Variáveis globais acessíveis a
O
Resumo popular: "use apenas um ponto" — evite código como:
pedido.getCliente().getEndereco().getCidade().getNome()
Isso é conhecido como "train wreck" (trem de vagões) — cada . é um vagão acoplado ao anterior. Se a estrutura interna de Cliente ou Endereco mudar, todo código que fez essa travessia quebra, mesmo estando em um módulo completamente não relacionado.
Porque isso importa na prática?
Quando o método M faz objeto.getX().getY().metodo(), ele passa a depender da estrutura interna de X e Y, não só da interface pública de O. Isso cria três problemas medidos empiricamente pela literatura de engenharia de software:
-
Acoplamento implícito:
Mestá acoplado a classes que nem sabia que existiam (as intermediárias). - Fragilidade: uma refatoração interna em qualquer elo da cadeia propaga quebras para código distante e não óbvio.
-
Dificuldade de teste: para testar
M, você precisa mockar toda a cadeia de objetos (Cliente,Endereco, etc.), não só oPedido.
Exemplo prático
Imagina duas coisas que existem no seu sistema:
- Um Cliente (a pessoa)
- Uma Carteira (que pertence ao cliente e guarda o dinheiro dele)
class Carteira {
saldo: number;
remover(valor: number) {
this.saldo-= valor;
}
}
class Cliente {
carteira: Carteira;
}
Jeito 1 (problemático)
Em outra parte do código, alguém quer cobrar R$ 50 do cliente. Ele escreve assim:
cliente.carteira.remover(50);
Repare o que essa linha está fazendo: ela pula dentro do Cliente, pega a Carteira dele, e mexe na Carteira diretamente.
Quem escreveu essa linha precisou saber duas coisas que não são da conta dele:
- Que Cliente tem uma carteira.
- Que a Carteira tem um método
remover.
Isso é o problema: código de fora está manipulando a carteira do cliente como se fosse dono dela.
Jeito 2 (correto)
class Cliente {
carteira: Carteira;
pagar(valor: number) {
this.carteira.remover(valor);
}
}
E quem quer cobrar o cliente agora escreve só:
cliente.pagar(50);
Pronto. Quem chamou essa linha não precisa saber que existe uma carteira. Só precisa saber que "cliente sabe pagar".
Por que o Jeito 2 é melhor? (o teste real)
Imagina que amanhã o sistema muda: em vez de Carteira, o cliente passa a ter um CartaoCredito.
-
No Jeito 1: toda parte do código que tinha escrito
cliente.carteira.remover(50)quebra, porquecarteiranão existe mais. Você precisa caçar essas linhas espalhadas pelo projeto inteiro e corrigir uma por uma. -
No Jeito 2: só a classe
Clienteprecisa mudar por dentro (trocarcarteira.removerporcartao.cobrar, por exemplo). Quem chamavacliente.pagar(50)continua funcionando sem nenhuma alteração.
// Jeito 2 depois da mudança interna — ninguém de fora percebeu nada
class Cliente {
cartao: CartaoCredito;
pagar(valor: number) {
this.cartao.cobrar(valor); // só mudou aqui dentro
}
}
A regra, em uma frase
Se para fazer uma coisa você precisa escrever
algo.parteDeDentro.outraParteDeDentro, é sinal de que você devia ter perguntado proalgose ele mesmo pode fazer isso por você.
Ou seja: cada . a mais na frase é uma aposta de que aquela estrutura interna nunca vai mudar. O princípio existe para você parar de apostar nisso.
Top comments (0)