Resumo : neste tutorial, você aprenderá sobre o Princípio de Inversão de Dependência C# que promove o desacoplamento de módulos de software.
Introdução ao Princípio de Inversão de Dependência C#
O Princípio de Inversão de Dependência (DIP) é o último princípio dos princípios SOLID:
- Princípio de Responsabilidade Única (SRP)
- Princípio Aberto-Fechado (OCP)
- Princípio de Substituição de Liskov (LSP)
- Princípio de segregação de interface (ISP)
- Princípio de Inversão de Dependência (DIP)
O Princípio da Inversão de Dependência afirma que módulos de alto nível não devem depender de módulos de baixo nível, mas ambos devem depender de abstrações.
Para entender o Princípio da Inversão de Dependência, primeiro você precisa entender os conceitos de módulos de alto e baixo nível.
Em geral, módulos de alto nível são módulos que contêm a lógica principal da aplicação, enquanto módulos de baixo nível são módulos que fornecem funcionalidade de suporte. Em outras palavras, os módulos de alto nível especificam o que o aplicativo fará, enquanto os módulos de baixo nível especificam como o aplicativo o fará.
Tradicionalmente, os módulos de baixo nível dependem de módulos de alto nível, pois os módulos de alto nível chamam os módulos de baixo nível para executar a funcionalidade necessária. No entanto, isso cria um acoplamento forte entre os dois módulos. Portanto, é difícil alterar um módulo sem afetar o outro.
O Princípio da Inversão de Dependência resolve este problema introduzindo uma camada de abstração entre os módulos de alto e baixo nível.
Esta camada de abstração é representada por uma interface, que define os métodos que o módulo de baixo nível deve implementar para fornecer sua funcionalidade. O módulo de alto nível depende desta interface em vez do módulo de baixo nível. Em outras palavras, o Princípio da Inversão de Dependência promove a dissociação dos módulos de software.
Exemplo de Princípio de Inversão de Dependência C#
O exemplo a seguir é uma ilustração da violação do princípio de inversão de dependência:
namespace DIP;
public class DatabaseService
{
public void Save(string message)
{
Console.WriteLine("Save the message into the database");
}
}
public class Logger
{
private readonly DatabaseService _databaseService;
public Logger(DatabaseService databaseService)
{
_databaseService = databaseService;
}
public void Log(string message)
{
_databaseService.Save(message);
}
}
public class Program
{
public static void Main(string[] args)
{
var logger = new Logger(new DatabaseService());
logger.Log("Hello");
}
}
Linguagem de código: C# ( cs )
Neste exemplo, temos duas classes principais DatabaseService
e Logger
:
- O
DatabaseService
é um módulo de baixo nível que fornece acesso ao banco de dados. - O
Logger
é um módulo de alto nível que registra dados.
A Logger
classe depende DatabaseService
diretamente da classe. Em outras palavras, o módulo de alto nível ( Logger
) depende do módulo de baixo nível ( DatabaseService
).
Em vez de fazer com que a Logger
classe dependa da DatabaseService
classe, podemos introduzir uma interface chamada IDataService
da qual ambas as classes dependem:
namespace DIP;
public interface IDataService
{
public void Save(string message);
}
public class DatabaseService: IDataService
{
public void Save(string message)
{
Console.WriteLine("Save the message into the database");
}
}
public class Logger
{
private readonly IDataService _dataService;
public Logger(IDataService dataService)
{
_dataService = dataService;
}
public void Log(string message)
{
_dataService.Save(message);
}
}
public class Program
{
public static void Main(string[] args)
{
var logger = new Logger(new DatabaseService());
logger.Log("Hello");
}
}
Linguagem de código: C# ( cs )
Neste exemplo:
Primeiro, defina a IDataAccess
interface que possui o Save()
método:
public interface IDataService
{
public void Save(string message);
}
Linguagem de código: C# ( cs )
Segundo, redefina o DatabaseAccess
que implementa a IDataAccess
interface:
public class DatabaseService: IDataService
{
public void Save(string message)
{
Console.WriteLine("Save the message into the database");
}
}
Linguagem de código: C# ( cs )
Terceiro, altere o membro e o construtor da Logger
classe para usar a IDataAccess
interface em vez da DatabaseAccess
classe:
public class Logger
{
private readonly IDataService _dataService;
public Logger(IDataService dataService)
{
_dataService = dataService;
}
public void Log(string message)
{
_dataService.Save(message);
}
}
Linguagem de código: C# ( cs )
Ao fazer isso, podemos desacoplar as classes Logger
e DatabaseService
, facilitando a alteração de uma classe sem afetar uma à outra.
Além disso, podemos facilmente trocar a DatabaseService
classe por uma classe diferente que implemente a IDataService
interface. Por exemplo, podemos definir FileService
uma classe que salva uma mensagem em um arquivo de texto, passando-a para a Logger
classe.
Resumo
- O Princípio da Inversão de Dependência afirma que módulos de alto nível não devem depender de módulos de baixo nível, mas ambos devem depender de abstrações.