C# -  A lei de Demeter (LoD)


 Hoje vamos recordar a lei de Demeter (lembra dela ???)

Segundo a Wikipedia a Lei de Demeter ou principio do conhecimento minimo é uma diretriz de desenvolvimento de software, particularmente de sistemas que utilizam a programação orientada a objetos.



Esta lei foi proposta por Ian Holland em 1987 durante a participação em um projeto chamado Demeter.

A Lei de Demeter é um princípio de design de software que estabelece que um objeto deve ter um conhecimento limitado sobre outros objetos e que só deve interagir com objetos próximos a ele. Essa lei é também conhecida como "Princípio do Menor Conhecimento" ou "Princípio de Não-Conhecimento" e visa a reduzir o acoplamento entre objetos e aumentar a coesão do código.

Na prática, isso significa que uma classe deve se comunicar com outras classes de forma indireta, através de métodos de objetos que ela já conhece, em vez de se comunicar diretamente com outras classes.

De acordo com a Lei de Demeter, um método de um objeto deve conhecer apenas os métodos e propriedades dos seguintes elementos:

- do próprio objeto;
- de um objeto que é passado como argumento para o método;
- de qualquer objeto criado dentro do método.

Por exemplo, se uma classe A precisa acessar um objeto da classe B, ela não deve chamar um método da classe B diretamente, mas sim chamar um método de um objeto intermediário que ela já conhece e que por sua vez conhece o objeto da classe B.



A lei de Demeter é conhecida como 'não fale com estranhos' porque porque seu princípio fundamental é que uma classe não deve conhecer a estrutura interna de outras classes e não deve se comunicar diretamente com objetos que ela não conhece.

Isso porque quando uma classe se comunica diretamente com outra classe que ela não conhece, ela se torna dependente da implementação dessa classe e acaba conhecendo a sua estrutura interna. Isso pode tornar o código mais acoplado e mais difícil de manter e evoluir.

Assim, seguindo o princípio da Lei de Demeter, uma classe deve se comunicar apenas com objetos que ela já conhece e em quem ela confia, ou seja, ela deve se comunicar apenas com seus "amigos" mais próximos. Esses "amigos" são os objetos que foram criados pela classe ou que foram passados como parâmetro para a classe. Ao se comunicar apenas com seus "amigos" mais próximos, a classe consegue manter um baixo acoplamento e tornar o código mais flexível e fácil de manter e evoluir.

Na linguagem C#, a Lei de Demeter pode ser aplicada de várias maneiras, como por exemplo:

- Utilizar interfaces para estabelecer contratos entre classes e limitar a exposição de detalhes de implementação;

- Evitar o uso de propriedades e métodos públicos que expõem detalhes internos de objetos;

- Usar injeção de dependência para passar objetos de outras classes como parâmetros em vez de criar objetos diretamente dentro de uma classe;

- Usar padrões de projeto como o padrão "Facade" para simplificar a interface de um sistema complexo e reduzir o acoplamento entre classes.

Ao seguir a Lei de Demeter, é possível tornar o código mais modular, fácil de entender e de manter, além de reduzir o risco de efeitos colaterais indesejados quando se fazem mudanças no código.

A seguir temos um exemplo de código que viola a lei de Demeter:

public class CarrinhoDeCompras
{
  
private List<Item> _itens = new List<Item>();
  
public void AdicionarItem(Item item)
   {
     _itens.Add(item);
   }
  
public void FecharCompra(Pagamento pagamento)
   {
    
var total = CalcularTotal();
     pagamento.RealizarPagamento(total);
     EnviarEmailConfirmacao();
   }

  
private decimal CalcularTotal()
   {
    
decimal total = 0;
    
foreach (var item in _itens)
     {
        total += item.Preco;
     }
    
return total;
   }

   private void EnviarEmailConfirmacao()
   {
     
var emailService = new EmailService();

     
var email = new Email("Você realizou uma compra no valor de R$"
                         + CalcularTotal());

      emailService.Enviar(email);
   }
}

public class Pagamento
{
  
public void RealizarPagamento(decimal valor)
   {
     
// Lógica de realização do pagamento
   }
}

public class Email
{
  
public Email(string conteudo)
   {
      Conteudo = conteudo;
   }

    public string Conteudo { get; set; }

}

public class EmailService
{
  
public void Enviar(Email email)
   {
     
// Lógica de envio do email
   }
}

public class Item
{
  
public decimal Preco { get; set; }
}

Neste exemplo, a classe CarrinhoDeCompras viola a Lei de Demeter ao criar diretamente uma instância da classe EmailService dentro do método EnviarEmailConfirmacao(). Ao fazer isso, a classe CarrinhoDeCompras está violando o princípio do "Menor Conhecimento" ao assumir o controle da criação e uso de um objeto que ela não deveria conhecer diretamente.

Para corrigir essa violação, seria necessário modificar o código da classe CarrinhoDeCompras para que ela dependesse de uma interface que representasse a funcionalidade de envio de e-mail, em vez de conhecer diretamente a implementação concreta da classe EmailService. Isso tornaria a classe CarrinhoDeCompras mais modular e fácil de entender, reduzindo a dependência entre classes e aumentando a coesão do código.

public interface IEmailService
{
  
void Enviar(Email email);
}

public class CarrinhoDeCompras
{
  
private List<Item> _itens = new List<Item>();
   private readonly IEmailService _emailService;

   public CarrinhoDeCompras(IEmailService emailService)
   {
     _emailService = emailService;
   }

  
public void AdicionarItem(Item item)
   {
     _itens.Add(item);
   }
  
public void FecharCompra(Pagamento pagamento)
   {
    
var total = CalcularTotal();
     pagamento.RealizarPagamento(total);
     EnviarEmailConfirmacao();
   }

  
private decimal CalcularTotal()
   {
    
decimal total = 0;
    
foreach (var item in _itens)
     {
        total += item.Preco;
     }
    
return total;
   }

   private void EnviarEmailConfirmacao()
   {
     
var email = new Email("Você realizou uma compra no valor de R$"
                         + CalcularTotal());

     
_emailService.Enviar(email);
   }
}

public class Pagamento
{
  
public void RealizarPagamento(decimal valor)
   {
     
// Lógica de realização do pagamento
   }
}

public class Email
{
  
public Email(string conteudo)
   {
      Conteudo = conteudo;
   }

    public string Conteudo { get; set; }

}

public class EmailService
{
  
public void Enviar(Email email)
   {
     
// Lógica de envio do email
   }
}

public class Item
{
  
public decimal Preco { get; set; }
}

Neste código refatorado agora a classe CarrinhoDeCompras foi modificada para depender de uma interface IEmailService em vez da implementação concreta da classe EmailService.

Isso permite que a classe CarrinhoDeCompras use um serviço de e-mail sem ter que conhecer a sua implementação concreta, reduzindo assim a violação da Lei de Demeter.

Além disso, a classe CarrinhoDeCompras agora recebe o serviço de e-mail por meio de injeção de dependência em seu construtor, o que torna o código mais modular e fácil de testar.

E estamos conversados ...

Referências:


José Carlos Macoratti