DIP - O Princípio da Inversão de Dependência

O DIP (Dependency Inversion Principle) é último dos 5 Princípios do acrônimo SOLID. Isso não faz deste princípio o menos ou mais importante que os demais, mas sim tão importante quanto. Definição do DIP Novamente, não trouxe nenhuma frase impactante escrita (ou dita) por alguma estrela do desenvolvimento e/ou arquitetura de software, mas podemos definir e afirmar que: Para reduzir o acoplamento, uma classe/tipo não deve depender diretamente de implementações concretas. As abstrações (interfaces ou classes abstratas) funcionam como contratos. Isso quer dizer que não importa como a implementação foi feita ou qual biblioteca está sendo usada, desde que a classe/tipo injetado seja capaz de atender o contrato estabelecido. Quando falamos de classes/tipos concretos, podemos ignorar aqueles que são considerados não voláteis, ou seja, que são estáveis e improváveis de sofrer alterações a qualquer momento por necessidades técnicas ou de negócio. Um exemplo de classe concreta considerada não volátil é StringBuilder. Apesar de implementar a interface ISerializable, ela é uma classe genérica, estável e improvável de sofrer mudanças drásticas em sua implementação. Além disso, não possui dependências externas que possam causar qualquer tipo de instabilidade. Agora, como de costume, vamos seguir com os nossos exemplos de violação e de aplicação. Caso não tenha entendido, peço que não se preocupe e eu garanto que você vai sair deste artigo totalmente esclarecido. Violação do DIP Vamos observar o seguinte código: public class EmailService { public void Send(string destination, string message) { // Lógica para realizar o envio de E-mail. } } public class Notifier { private readonly EmailService _emailService; public Notifier(EmailService emailService) { _emailService = emailService; } public void Notify(string destination, string message) { _emailService.Send(destination, message); } } Podemos notar que a classe Notifier tem uma dependência direta da classe EmailService. Sempre que a EmailService for alterada ou um novo serviço de notificação for implementado, a classe Notifier será obrigada a ser modificada e recompilada pois está fortemente acoplada à implementação da classe EmailService. Como já falamos em outros artigos, um dos nossos principais objetivos é evitar alto acoplamento no código. Um sistema fortemente acoplado torna-se difícil de manter, aumentando a chance de introduzir erros ao alterar algo que já está validado e publicado no ambiente de produção, violando também o OCP (Princípio Aberto/Fechado). Aplicação do DIP Agora, vamos refatorar esse código para seguir o Princípio da Inversão de Dependência (DIP) e observar os benefícios que essa abordagem traz para o design do software: public interface INotificationService { void Send(string destination, string message); } public class EmailService : INotificationService { public void Send(string destination, string message) { // Lógica para realizar o envio de E-mail. } } public class Notifier { private readonly INotificationService _notificationService; public Notifier(INotificationService notificationService) { _notificationService = notificationService; } public void Notify(string destination, string message) { _notificationService.Send(destination, message); } } Após a refatoração, a classe Notifier depende agora de uma interface chamada INotificationService. Pouco importa se a implementação injetada enviará notificações via e-mail, SMS ou até mesmo sinal de fumaça. O que importa é que a implementação atenda ao contrato definido pela interface, implementando o método Send, que recebe os parâmetros referentes ao destino e à mensagem que deve ser enviada. Fácil, não é mesmo?

Jan 16, 2025 - 12:00
DIP - O Princípio da Inversão de Dependência

O DIP (Dependency Inversion Principle) é último dos 5 Princípios do acrônimo SOLID. Isso não faz deste princípio o menos ou mais importante que os demais, mas sim tão importante quanto.

Definição do DIP

Novamente, não trouxe nenhuma frase impactante escrita (ou dita) por alguma estrela do desenvolvimento e/ou arquitetura de software, mas podemos definir e afirmar que:

Para reduzir o acoplamento, uma classe/tipo não deve depender diretamente de implementações concretas.

As abstrações (interfaces ou classes abstratas) funcionam como contratos. Isso quer dizer que não importa como a implementação foi feita ou qual biblioteca está sendo usada, desde que a classe/tipo injetado seja capaz de atender o contrato estabelecido.

Quando falamos de classes/tipos concretos, podemos ignorar aqueles que são considerados não voláteis, ou seja, que são estáveis e improváveis de sofrer alterações a qualquer momento por necessidades técnicas ou de negócio.

Um exemplo de classe concreta considerada não volátil é StringBuilder. Apesar de implementar a interface ISerializable, ela é uma classe genérica, estável e improvável de sofrer mudanças drásticas em sua implementação. Além disso, não possui dependências externas que possam causar qualquer tipo de instabilidade.

Image description

Agora, como de costume, vamos seguir com os nossos exemplos de violação e de aplicação. Caso não tenha entendido, peço que não se preocupe e eu garanto que você vai sair deste artigo totalmente esclarecido.

Violação do DIP

Vamos observar o seguinte código:

public class EmailService 
{
    public void Send(string destination, string message) 
    {
        // Lógica para realizar o envio de E-mail.
    }
}

public class Notifier 
{
    private readonly EmailService _emailService;

    public Notifier(EmailService emailService) 
    {
        _emailService = emailService;
    }

    public void Notify(string destination, string message) 
    {
        _emailService.Send(destination, message);
    }
}

Podemos notar que a classe Notifier tem uma dependência direta da classe EmailService. Sempre que a EmailService for alterada ou um novo serviço de notificação for implementado, a classe Notifier será obrigada a ser modificada e recompilada pois está fortemente acoplada à implementação da classe EmailService.

Image description

Como já falamos em outros artigos, um dos nossos principais objetivos é evitar alto acoplamento no código. Um sistema fortemente acoplado torna-se difícil de manter, aumentando a chance de introduzir erros ao alterar algo que já está validado e publicado no ambiente de produção, violando também o OCP (Princípio Aberto/Fechado).

Aplicação do DIP

Agora, vamos refatorar esse código para seguir o Princípio da Inversão de Dependência (DIP) e observar os benefícios que essa abordagem traz para o design do software:

public interface INotificationService 
{
    void Send(string destination, string message);
}

public class EmailService : INotificationService 
{
    public void Send(string destination, string message) 
    {
        // Lógica para realizar o envio de E-mail.
    }
}

public class Notifier 
{
    private readonly INotificationService _notificationService;

    public Notifier(INotificationService notificationService) 
    {
        _notificationService = notificationService;
    }

    public void Notify(string destination, string message) 
    {
        _notificationService.Send(destination, message);
    }
}

Após a refatoração, a classe Notifier depende agora de uma interface chamada INotificationService. Pouco importa se a implementação injetada enviará notificações via e-mail, SMS ou até mesmo sinal de fumaça. O que importa é que a implementação atenda ao contrato definido pela interface, implementando o método Send, que recebe os parâmetros referentes ao destino e à mensagem que deve ser enviada.

Fácil, não é mesmo?