C#
- Inversion of Control e Dependency Injection
![]() |
Hoje vamos revisitar os conceitos de IoC e Dependency Injection. |
A Dependency Injection, também conhecido como DI, é um Design Pattern usado para evitar o acoplamento em seu código.
Uma dependência é um objeto que sua classe precisa para fazer o trabalho, ou seja, este objeto não é criado dentro de sua classe. A dependência é injetada em uma classe que a usa, portanto, a dependência deve ser externa. A instanciação do objeto (ou seja, a dependência) não deve estar dentro da classe, mas fora dela.
Veja o exemplo a seguir:
[Route("api/[controller]")]
[ApiController]
public class OrdersController : ControllerBase
{
private readonly NorthwindContext _context;
public OrdersController(NorthwindContext context)
{
_context = context;
}
...
}
|
Temos neste código o exemplo clássico da injeção da dependência via construtor da classe.
Neste código, ao instanciar a classe OrdersController, uma instância do tipo NorthwindContext será injetada no construtor através do parâmetro context que vai atribuir o seu valor para a variável _context que poderá ser usada nos demais métodos da classe.
Mas quem vai fazer o trabalho de injetar a instância ? (Alguém tem que fazer o trabalho pesado!!!)
Neste exemplo será o contêiner DI da ASP .NET Core que vai injetar a instância da classe/serviço. Existem outros contêineres DI que podemos usar como : Unity, Castle Windsor, StructureMap, AutoFac, Spring.NET, etc.
É um detalhe bem simples mas usar a injeção de dependência de traz os seguintes benefícios:
A injeção via construtor talvez seja a forma mais usada de aplicar este recurso e tem as seguintes vantagens:
No entanto existem mais três formas de realizar a injeção de dependência que veremos a seguir:
1- Injeção de dependência usando propriedades
A implementação da injeção de dependência via propriedades - Setter Injection - não força a dependência ser passada para o construtor. Ao invés disso, as dependências são definidas em propriedades públicas expostas pelo objeto.
Esta abordagem tem as seguintes motivações:
Esta abordagem deve ser usada com cautela pois não se têm uma clara idéia de quais dependências são necessárias e se ocorrer alguma exceção é difícil rastrear.
Neste código estamos injetando a dependência através da propriedade pública da classe Cliente
A desvantagem desta abordagem é que os objetos são expostos publicamente e isso quebra a regra de encapsulamento da programação orientada a objeto.
2 - Injeção de dependência via interface
A implementação da injeção de dependência via interface - Interface Injection - utiliza uma interface comum que outras classes necessitam implementar para injetar a dependência.
Para este exemplo vou usar o seguinte cenário: Considere um ambiente com duas camadas :
interface IBLL { } class ProdutoBLL : IBLL {} class ClienteBLL : IBLL {} class CamadaUI : IUI { private IBLL camadaNegocios; public void SetObjectBLL (IBLL camadaNegocios); { this.camadaNegocios = camadaNegocios; } } |
No código acima temos que o método SetObjectBLL da classe CamadaUI aceita um parâmetro do tipo IBLL.
A seguir temos exemplos de como podemos chamar o método SetObjectBLL para injetar a dependência para qualquer tipo de classe IBLL:
BLL camadaNegocios = new
ProductoBLL(); CamdaUI camadaUI = new CamadaUI(); camdaUI.SetObjectBLL(camadaNegocios); ou IBLL
camadaNegocios = new ClienteBLL(); |
Nesta implementação usando interfaces estamos passando uma referência para um tipo IBLL ao invés de uma instância do tipo.
3- Service Locator
A injeção de dependência via Service Locator usa um registro central conhecido como "localizador de serviço" que, mediante solicitação, retorna as informações necessárias para executar uma determinada tarefa.
A seguir temos uma implementação bem simples de Service Locator:
{ T GetService<T>(); } |
A seguir a implementação do contrato :
class ServiceLocator : IServiceLocator
{
private IDictionary<object, object> services;
internal ServiceLocator()
{
services = new Dictionary<object, object>();
this.services.Add(typeof(IServiceA), new ServiceA());
this.services.Add(typeof(IServiceB), new ServiceB());
this.services.Add(typeof(IServiceC), new ServiceC());
}
public T GetService<T>()
{
try
{
return (T)services[typeof(T)];
}
catch (KeyNotFoundException)
{
throw new ApplicationException("Serviço solicitado não esta registrado");
}
}
}
|
Neste código o
construtor da classe registra todos os serviços disponíveis em um dicionário. Em
nosso exemplo, temos 3 serviços diferentes acessíveis por meio de
IServiceA, IServiceB e IServiceC. (Presume-se aqui que ServiceA
implementa IServiceA e assim por diante.)
O método GetService() retorna uma referência da
implementação correta buscando-a no dicionário
É assim que um cliente chamaria o serviço:
static void Main(string[]
args) { IServiceLocator locator = new ServiceLocator(); IServiceA myServiceA = locator.GetService<IServiceA>(); } |
Os clientes não conhecem as classes reais que implementam o serviço. Eles só precisam interagir com o localizador de serviço para chegar a uma implementação.
Ele é considerado um antipadrão sendo usado para encapsular os processos envolvidos na obtenção de um serviço com uma camada de abstração.
"Não estejais
inquietos por coisa alguma; antes as vossas petições sejam em tudo conhecidas
diante de Deus pela oração e súplica, com ação de graças."
Filipenses 4:6
Referências:
C# - Tasks x Threads. Qual a diferença
VB .NET - Datas, horas: conceitos e operações
C# - Programação Assíncrona como : Asycn e Task
O tratamento de datas no VB.NET
C# - Obtendo a data e a hora por TimeZone
C# - O Struct Guid
C# - Checando Null de uma forma mais elegante e concisa
DateTime - Macoratti.net
Null o que é isso ?
Formatação de data e hora para uma cultura ...
C# - Calculando a diferença entre duas datas
NET - Padrão de Projeto - Null Object Pattern
C# - Fundamentos : Definindo DateTime como Null ...
C# - Os tipos Nullable (Tipos Anuláveis)