Aplicando a Lei de Demeter na Programação Orientada a Objetos com Rails
Na programação orientada a objetos (OOP), aderir a princípios que promovem baixo acoplamento e encapsulamento é essencial para construir sistemas escaláveis e de fácil manutenção. Um desses princípios é a Lei de Demeter, também conhecida como o Princípio do Menor Conhecimento. Esse princípio ajuda a evitar que objetos saibam demais sobre o funcionamento interno de outros objetos, um problema comum na OOP.
Neste post, vamos explorar a Lei de Demeter e demonstrar como aplicá-la em uma aplicação Ruby on Rails. Ao final, você entenderá como esse princípio pode simplificar seu código e tornar sua aplicação mais fácil de manter.
O que é a Lei de Demeter na Programação Orientada a Objetos?
Na programação orientada a objetos, a Lei de Demeter (LoD) pode ser resumida com a seguinte regra: "Um objeto deve se comunicar apenas com seus amigos imediatos e não com estranhos."
Para ser mais claro, um objeto deve invocar métodos apenas em:
- Ele mesmo.
- Suas dependências diretas (campos ou variáveis de instância).
- Objetos passados como argumentos.
- Objetos que ele cria.
Essa regra reduz as dependências entre objetos, tornando o código menos frágil e mais fácil de manter. Quando um objeto "atravessa" outro para acessar o método de um terceiro objeto (ex.: pedido.cliente.endereco.cidade
), cria-se um acoplamento forte. Esse tipo de código é mais difícil de testar, estender e depurar.
O Problema: Quebrando a Lei de Demeter
Vamos analisar um exemplo comum em uma aplicação Rails onde a Lei de Demeter é violada. Suponha que temos três modelos: Pedido
, Cliente
e Endereco
. Um Pedido pertence a um Cliente, e um Cliente possui um Endereço. No código abaixo, o Pedido calcula um preço de envio com desconto baseado no endereço do cliente.
class Pedido
def preco_envio_com_desconto(codigo_desconto)
cupom = Cupom.new(codigo_desconto)
cupom.desconto(cliente.endereco.preco_envio)
end
end
Essa implementação viola a Lei de Demeter. O objeto Pedido
está acessando Cliente
para chegar a Endereco
e buscar o preco_envio
. Isso leva a uma situação em que Pedido
sabe demais sobre a estrutura interna de Cliente
e Endereco
, resultando em um forte acoplamento entre esses objetos.
Refatoração: Aplicando a Lei de Demeter
Para corrigir isso, podemos mover a responsabilidade de calcular o preço de envio com desconto para o modelo Cliente
. Dessa forma, Pedido
interage apenas com seu colaborador direto, Cliente
, enquanto Cliente
gerencia sua relação com Endereco
.
Aqui está o código refatorado:
class Pedido
def preco_envio_com_desconto(codigo_desconto)
cliente.preco_envio_com_desconto(codigo_desconto)
end
end
class Cliente
def preco_envio_com_desconto(codigo_desconto)
cupom = Cupom.new(codigo_desconto)
cupom.desconto(endereco.preco_envio)
end
end
Agora, a classe Pedido
não acessa diretamente Endereco
. Em vez disso, delega o cálculo ao objeto Cliente
, o que reduz o acoplamento e torna o sistema mais fácil de manter.
Usando delegate
para um Código Ainda Mais Limpo
Podemos levar essa refatoração um passo adiante usando o método delegate
do Rails. O método delegate
permite encaminhar chamadas de métodos automaticamente de um objeto para outro, tornando o código mais limpo e fácil de ler.
Veja como podemos refatorar o exemplo anterior usando delegate
:
class Pedido
belongs_to :cliente
# Delegar preco_envio_com_desconto ao cliente
delegate :preco_envio_com_desconto, to: :cliente
end
class Cliente
has_one :endereco
def preco_envio_com_desconto(codigo_desconto)
cupom = Cupom.new(codigo_desconto)
cupom.desconto(endereco.preco_envio)
end
end
Como Isso Funciona?
- Em
Pedido
: A linhadelegate :preco_envio_com_desconto, to: :cliente
permite quePedido
delegue a chamada paraCliente
sem precisar definir o método manualmente. - Em
Cliente
: O métodopreco_envio_com_desconto
gerencia a interação comEndereco
e aplica a lógica de desconto.
Essa refatoração torna o código mais limpo, ao mesmo tempo em que respeita a Lei de Demeter.
Por Que Seguir a Lei de Demeter?
- Baixo Acoplamento
Seguindo a Lei de Demeter, os objetos não precisam conhecer os detalhes internos de outros objetos. Isso reduz a probabilidade de que uma mudança em uma parte do sistema quebre outra parte. Por exemplo, se alterarmos a estrutura deEndereco
,Pedido
não precisará ser modificado. - Encapsulamento
Cada classe é responsável por sua própria lógica. No nosso exemplo, a classeCliente
é responsável por gerenciar o endereço e o preço de envio, enquantoPedido
foca nos detalhes do pedido. Essa clara separação de responsabilidades torna o código mais fácil de entender e manter. - Melhor Manutenibilidade
Menos conhecimento sobre as estruturas de outros objetos significa menos dependências. Isso torna o código mais flexível e mais fácil de refatorar no futuro. Se a lógica de negócios em torno do envio mudar, você só precisará atualizar o modeloCliente
, sem impactarPedido
.
Conclusão
A Lei de Demeter é um princípio chave na programação orientada a objetos que ajuda a reduzir o acoplamento e a promover uma melhor organização do código. Em aplicações Rails, aderir a esse princípio resulta em sistemas mais escaláveis e fáceis de manter. Refatorar seu código para delegar responsabilidades e limitar o conhecimento de um objeto sobre os outros cria uma arquitetura de aplicação mais limpa e robusta.
No nosso exemplo com Pedido
, Cliente
e Endereco
, vimos como é fácil quebrar a Lei de Demeter e como o uso do método delegate
do Rails pode ajudar a corrigir isso. À medida que sua aplicação Rails cresce, aplicar a Lei de Demeter garantirá que sua base de código permaneça limpa, fácil de manter e extensível.
Principais Conclusões:
- A Lei de Demeter incentiva que objetos se comuniquem apenas com seus colaboradores imediatos, reduzindo as dependências.
- Violar essa lei resulta em código fortemente acoplado, que é mais difícil de manter.
- Usar o método
delegate
do Rails facilita a aplicação desse princípio, resultando em um código mais limpo e modular.