Я был виновен в наличии отношения 1 к 1 между моими интерфейсами и конкретными классами при использовании инъекции зависимостей. Когда мне нужно добавить метод к интерфейсу, я в конечном итоге сломаю все классы, реализующие интерфейс.
Это простой пример, но предположим, что мне нужно ввести ILogger
в один из моих классов.
public interface ILogger
{
void Info(string message);
}
public class Logger : ILogger
{
public void Info(string message) { }
}
Наличие отношений 1 к 1, подобных этому, похоже на запах кода. Поскольку у меня только одна реализация, есть ли потенциальные проблемы, если я создаю класс и отмечаю метод Info
как виртуальный, чтобы переопределить в моих тестах вместо того, чтобы создавать интерфейс только для одного класса?
public class Logger
{
public virtual void Info(string message)
{
// Log to file
}
}
Если мне нужна другая реализация, я могу переопределить метод Info
:
public class SqlLogger : Logger
{
public override void Info(string message)
{
// Log to SQL
}
}
Если каждый из этих классов имеет определенные свойства или методы, которые создавали бы пропущенную абстракцию, я мог бы извлечь базовый класс:
public class Logger
{
public virtual void Info(string message)
{
throw new NotImplementedException();
}
}
public class SqlLogger : Logger
{
public override void Info(string message) { }
}
public class FileLogger : Logger
{
public override void Info(string message) { }
}
Причина, по которой я не отмечал базовый класс как абстрактный, состоит в том, что если бы я хотел добавить другой метод, я бы не нарушил существующие реализации. Например, если мой FileLogger
нужен метод Debug
, я могу обновить базовый класс Logger
без нарушения существующего SqlLogger
.
public class Logger
{
public virtual void Info(string message)
{
throw new NotImplementedException();
}
public virtual void Debug(string message)
{
throw new NotImplementedException();
}
}
public class SqlLogger : Logger
{
public override void Info(string message) { }
}
public class FileLogger : Logger
{
public override void Info(string message) { }
public override void Debug(string message) { }
}
Опять же, это простой пример, но когда я должен предпочесть интерфейс?