Ответ
Принимать интерфейс в качестве зависимости следует для достижения слабой связанности (loose coupling) и соблюдения принципа инверсии зависимостей (DIP). Это позволяет:
- Абстрагироваться от конкретной реализации. Клиентский код зависит только от контракта (методов интерфейса), а не от деталей реализации.
- Упростить тестирование. Зависимость легко подменить mock- или stub-объектом в юнит-тестах.
- Повысить гибкость архитектуры. Реализации можно менять или добавлять новые, не затрагивая существующий клиентский код.
Пример на C#:
// Контракт
public interface ILogger
{
void Log(string message);
}
// Конкретная реализация №1
public class FileLogger : ILogger
{
public void Log(string message) => File.AppendAllText("app.log", message);
}
// Конкретная реализация №2
public class ConsoleLogger : ILogger
{
public void Log(string message) => Console.WriteLine($"[LOG] {message}");
}
// Сервис, зависящий от абстракции (интерфейса)
public class OrderService
{
private readonly ILogger _logger;
// Внедрение зависимости через конструктор
public OrderService(ILogger logger)
{
_logger = logger;
}
public void PlaceOrder(Order order)
{
// Логика размещения заказа...
_logger.Log($"Order {order.Id} placed.");
}
}
// Использование: можно легко подменить реализацию логгера.
var serviceWithFileLog = new OrderService(new FileLogger());
var serviceWithConsoleLog = new OrderService(new ConsoleLogger());