SOLID na Prática! (Parte 5: Dependency Inversion)

Thiago Barradas
thiagobarradas
Published in
4 min readMay 7, 2021
Vamos falar de SOLID, além do conceito! Vamos para a prática, agora com o princípio Dependency Inversion!

Este artigo é uma continuação de SOLID na Prática! (Parte 4: Interface Segregation).

Dependency Inversion Principle

“Deve-se depender de abstrações, não de objetos concretos.”

Se você chegou até este artigo e leu os anteriores, já deve ter percebido o quanto abstrair, criar interfaces, é algo extremamente importante para um software escalável. Uma dependência existe basicamente quando um componente precisa de um outro para o seu funcionamento, porém existem diversos fatores que podem causar um alto nível de acoplamento.

Vamos tentar ilustrar da seguinte maneira: Se você quer sair no final de semana para o shopping para fazer compras você depende de uma forma de pagamento (entre no jogo, imagine que todas formas de pagamento são aceitas nesse shopping hehe).“Forma de pagamento” seria uma abstração, dado que você pode tê-la de forma “concreta”, como: Dinheiro em espécie, cartão de crédito, cheque, celular com apps de pagamento, etc.

Perceba que se eu digo que para ir ao shopping você depende de um cartão de crédito, estou limitando quando você pode ir e você fica “dependente” de um único recurso que “não é o único recurso que pode resolver o seu problema”. O seu objetivo é fazer compras e pra isso você precisar apenas pagar. Para pagar você precisa apenas ter uma forma de pagamento, independente de qual seja.

Seguindo o exemplo, veja um código com dependências (ou seja, que fere o princípio da inversão de dependências):

SOLID [D] — Exemplo com alto acoplamemto.

Neste código, somente podemos pagar com cartão de crédito e ficamos totalmente dependentes dele e de sua implementação.

SOLID [D] — Exemplo com alto acoplamemto e sem injeção.

O código acima consegue ser ainda pior do que o primeiro. Além de depender do cartão de crédito é impossível controlar o que está chegando como dependência. Ele fere a invesão de dependência e impossibilita a injeção da dependência.

Não confuda inversão de dependência com injeção de dependência. Inversão de dependência é um conceito, um princípio, que diz que uma dependência de ser flexível e depender de abstrações enquanto injeção de dependência é um design pattern, que está ligado com o fato de ser possível inserir a dependência a partir de um local externo à classe, normalmente via construtor. É importante entender a diferença, porém ambos acabam andando bem juntinhos na prática!

Vamos ver um exemplo certo agora?

SOLID [D] — Exemplo de implementação correta com baixo acoplamemto.

Nesse novo código criamos a abstração para a “forma de pagamento” e a classe Person passou a depender da abstração (IPaymentMethod) e não mais de uma implementação específica. Com essa estrutura é possível criar uma nova instância de Person com cartão de crédito (CreditCard) ou com dinheiro (Cash) como visto no final do snippet acima.

Dessa maneira, as possibilidades para ampliar as formas de pagamento é muito maior e muito mais fácil de se evoluir. Basta criar uma nova classe que implementa a interface IPaymentMethod e essa nova forma de pagamento já poderá ser passada como parâmetro para a classe Person.

DTOs

A única exceção que temos para “não necessitar” de uma abstração são os DTOs (data transfer object — objetos usados para trafegar informação pelo sistema, no geral constituído apenas de propriedades). Classes podem depender de DTOs pois são apenas as informações que seriam passadas separadamentes e foram agrupadas em uma classe, sem implementações.

Como saber?

Como saber se estou fazendo Inversão de Dependência?

  • Dependa sempre de abstrações. É isso!

Como saber se estou fazendo Injeção de Dependência?

  • Além fazer inversão de dependência — depender de abstrações — receba as dependências através do construtor.
  • Se você observar a palavra-chave new instanciando algum objeto em qualquer parte da sua classe, desconfie! Se não for um DTO, estará errado.
  • Use algum recurso (normalmente tem-se nos próprios frameworks ou é possível obter como um pacote adicional no Nuget, Maven, Composer, Npm, etc) para “registrar” qual implementação padrão é utilizada para determinada abstração e suas peculiaridades.

Testes Unitários

Testes unitários devem apenas testar a unidade atual (um método de uma determinada classe) e não deve, jamais, depender de implementações externas.

A inversão de dependência permite que qualquer classe concreta que tenha o comportamento definido na abstração possa ser utilizada (como dubladores — vulgo mock — para simular o seu comportamento) e a injeção de dependência permitirá que essa “classe simuladora” seja passada via construtor para ocupar lugar da dependência principal, fazendo com que o teste valide somente o código chamado e tudo que for externo, na verdade, tenha um comportamento simulado.

Maaaaaaaaaaaaaaaas, Injeção de Dependência, Testes Unitários, Design Patterns e mais um monte de coisa dita por aqui, são assuntos para outros artigos.

Se você chegou até aqui e leu todos os artigos da série SOLID, parabéns!!! 👏 O próximo passo é você começar a aplicar todos esses conceitos no seu dia-a-dia e fazer códigos mais robustos, escaláveis, flexíveis, com baixo nível de acoplamento, de fácil manutenabilidade e evolutívo.

Além do SOLID é muito importante ter na veia os princípios do Clean Code. Esses são pontos essenciais para TODOS desenvolvedores de software.

--

--

Thiago Barradas
thiagobarradas

Microsoft MVP, Elastic Contributor. Entusiasta de novas tecnologias e arquiteto de software na Stone. Cultuador do hábito de formar cabeças pensantes.