.NET - Inversão de controle e Injeção de dependência para iniciantes


Você já ouviu falar de Inversão de Controle (IC) ? e de Injeção de Dependência(ID) ?

Já ouviu falar que que IC e ID são a mesma coisa ? ou não ?

Bem, este artigo trata do assunto de uma forma bem objetiva e sem rodeios apresentando os principais conceitos envolvidos nessa discussão.

A IC e a ID não são a mesma coisa.

A Inversão de controle(Ioc) é um princípio e injeção de dependência(ID) é uma forma de implementar a inversão de controle ou seja: geralmente fazemos a Inversão de controle utilizando a injeção de dependência.

Vamos montar um cenário bem ingênuo para explicar o assunto mais uma vez...

Considere a class DAL, cujo código vemos abaixo, na versão C# e VB .NET:

 public class DAL
    {
        private cSqlServer _sql; 

        public DAL()
        {
            _sql = new cSqlServer(); 
        }
    }
Public Class DAL

    Private _sql As cSqlServer

    Public Sub New()
        _sql = New cSqlServer()
    End Sub

End Class
C# VB .NET

No exemplo acima, onde temos a classe DAL. O construtor padrão da classe DAL cria um objeto da classe cSqlServer.

Isso significa que a classe DAL é responsável pela criação de um objeto da classe SqlServer.

Portanto, existe um forte acoplamento entre a classe DAL e classe SqlServer.

Isso nos trás os seguintes problemas...

1- A Classe DAL é responsável pela criação de um objeto da classe SqlServer;
2- A Classe SqlServer está diretamente referenciada na classe DAL;
3- A classe DAL deveria estar ciente do tipo de classe SqlServer;

Acoplamento

- Acoplamento é o nível de dependência/conhecimento que pode existir entre as classes;
- Uma classe com acoplamento fraco não é dependente de muitas classes para fazer o que ele tem que fazer;
- Uma classe com acoplamento forte depende de muitas outras classes para fazer o seu serviço;
- Uma classe com
acoplamento forte é mais difícil de manter, de entender e de ser reusada;

Coesão

- Coesão é o nível de integralidade interna de uma classe; (Veja o principio da responsabilidade única - SRP)
- A coesão Mede o grau que um classe ou seus métodos fazem sentido, ou seja, quão claro é o entendimento do que a classe ou método possui
- Uma classe com alta coesão possui responsabilidades bem definidas e são difíceis de serem desmembradas em outras classes;
- Uma classe com baixa coesão possui muitas responsabilidades, geralmente que pertencem a outras classes, e podem ser facilmente desmembradas em outras classes;
- Uma classe com
baixa coesão é difícil de entender, manter e reusar;

Portanto quanto mais forte o acoplamento e mais baixa a coesão de uma classe mais difícil ela será de entender, manter e ser reusada.

Neste exemplo temos um forte acoplamento entre a classe DAL e a classe cSqlServer pois quem controla a criação de uma instância da classe cSqlServer é a classe DAL.

A classe DAL é responsável por obter uma referência a classe cSqlServer.

Qualquer alteração que ocorra na classe cSqlServer vai afetar diretamente a classe DAL pois esta possui uma referência a classe cSqlServer.

Como fazer com que a classe DAL não seja afetada pelas mudanças feitas na classe cSqlServer sem perder a referência para a classe cSqlServer ?

Como diminuir o acoplamento entre a classe DAL e a classe cSqlServer ?

A solução é mudar a parte da criação do objeto na classe DAL.

Precisamos mudar o controle de criação de objetos e podemos fazer isso usando o padrão inversão de controle (IoC).

Os princípios da Inversão de Controle (IoC) podem ser expressos da seguinte forma:

1- Classes principais agregando outras não devem depender da implementação direta dessas classes, ambas as classes devem depender de uma abstração;

2- Uma abstração não deve depender dos detalhes, os detalhes devem depender da abstração;

Mas onde entra a injeção de dependência nesta história ?

A Inversão de controle é implementada por meio da injeção de dependência, porque Inversão de controle é um princípio e a injeção de dependência é uma forma de implementar a IoC.

Como implementar o inversão de controle ?

Uma das formas de implementar a inversão de controle é através da injeção de dependência (DI) que nos trás os seguintes benefícios;

Em suma, a DI isola a implementação de um objeto da construção do objeto do qual ele depende.

Podemos implementar a injeção de dependência das seguintes maneiras:

A figura abaixo mostra um esquema de omo implementar a IoC(inversion of control) usando as 4 formas de injeção de dependência (DI-dependency injection):

Vejamos um resumo da aplicação de cada uma das formas de implementação da da IoC via DI:

1- Aplicando IoC com DI via Construtor

public class DAL
{
        private ISql _sql;   
        public DAL(ISql obj)
        {
             _sql = obj;
        }
} 
Public Class DAL
	Private _sql As ISql
	Public Sub New(obj As ISql)
		_sql = obj
	End Sub
End Class
C# VB .NET

Nesta metodologia, passamos um objeto SQL para a classe DAL.

No código acima você pode ver que há um construtor com parâmetros na classe DAL.

Com isso não existe mais um acoplamento forte entre essas classes.

Este método não é útil se somente pode usar um construtor padrão.

2- Aplicando IoC com DI via get/set

public class DAL
{
    private ISql _sql;   
    public Isql Sql
    {
        set
        { 
             _sql = value; 
        }
    }
}
Public Class DAL
    Private _sql As ISql
    Public WriteOnly Property Sql() As Isql
	Set
		_sql = value
	End Set
   End Property
End Class
C# VB .NET

Neste método expomos um objeto SQL através dos métodos get/set da classe DAL.

Mas ela viola a regra de encapsulamento da OOP. Encapsulamento significa esconder detalhes internos de um objeto.

Então, aqui, em vez de esconder um objeto, estamos expondo um objeto.

3- Aplicando IoC com DI via Interface

interface ISqlDI
{
    void setConnection(ISql obj);
}
public class DAL : ISqlDI
{
    private ISql _sql;    
    public void setConnection(ISql obj)
    {
        _sql = obj;
    }
}
Interface ISqlDI
	Sub setConnection(obj As ISql)
End Interface
Public Class DAL
    Implements ISqlDI

    Private _sql As ISql

    Public Sub setConnection1(obj As ISql) Implements ISqlDI.setConnection
        _sql = obj
    End Sub
End Class
C# VB .NET

Neste método implementamos uma interface que tem um método setConnection que define o objeto SQL.

A classe DAL implementa uma interface SQL. Assim, com a ajuda do método setConnection o cliente pode injetar um objeto SQL na classe DAL.

4- Aplicando IoC com DI via Service Locator

static class LocateConnection
{
    public static ISql getConnection() { }
}
public class DAL
{
    private ISql _sql;
    public DAL()
    {
        _sql = LocateConnection.getConnection();
    }
}
NotInheritable Class LocateConnection
	Public Shared Function getConnection() As ISql
	End Function
End Class

Public Class DAL
	Private _sql As ISql

	Public Sub New()
		_sql = LocateConnection.getConnection()
	End Sub
End Class
C# VB .NET

Neste método criamos uma classe estática - LocateConnection - e um método estático - getConnection - nesta desta classe.

A classe DAL chama esse método estático do seu construtor padrão , e , desta forma o objeto SQL é injetado na classe DAL.

Dessa forma vimos um resumo sobre o conceito da Inversão de controle e as formas de implementar a IoC usando a injeção de dependência(DI).

Heb 2:1 Por isso convém atentarmos mais diligentemente para as coisas que ouvimos, para que em tempo algum nos desviemos delas.

Heb 2:2 Pois se a palavra falada pelos anjos permaneceu firme, e toda transgressão e desobediência recebeu justa retribuição,

Heb 2:3 como escaparemos nós, se descuidarmos de tão grande salvação? A qual, tendo sido anunciada inicialmente pelo Senhor, foi- nos depois confirmada pelos que a ouviram:

Heb 2:4 testificando Deus juntamente com eles, por sinais e prodígios, e por múltiplos milagres e dons do Espírito Santo, distribuídos segundo a sua vontade.

Referências:


José Carlos Macoratti