Является ли принцип разделения интерфейса в отношении классов или объектов?
Напомнить (из вики):
Принцип сегрегации интерфейса (ISP) гласит, что ни один клиент не должен зависеть от методов, которые он не использует.
А теперь посмотри на мой пример.
Вот моя изменчивая сущность. Он редактируется откуда-то и может уведомлять об изменениях через интерфейс только для чтения:
interface ICounter
{
event Action<int> NewNumber;
}
class Counter : ICounter
{
public event Action<int> NewNumber;
int number = 0;
public void IncrementAndSend(int x)
{
number += x;
if (NewNumber != null) NewNumber(number);
}
}
А вот класс из транспортного уровня, который его использует. Посмотрите на два варианта инъекций (Attach_1
а также Attach_2
) и мои предположения ниже:
class OneToManyRouter
{
IEnumerable<Counter> _destinations;
public OneToManyRouter(IEnumerable<Counter> destinations)
{
_destinations = destinations;
}
// 1
public void Attach_1(ICounter source)
{
source.NewNumber += (n) =>
{
foreach (var dest in _destinations) dest.IncrementAndSend(n);
};
}
// 2
public void Attach_2(Counter source)
{
source.NewNumber += (n) =>
{
foreach (var dest in _destinations) dest.IncrementAndSend(n);
};
}
}
- Интернет-провайдер о реальных объектах. Вы не должны использовать "чрезмерные" ссылки на входящие параметры.
- Интернет-провайдер о занятиях. Если ваш класс где-то уже использует полный интерфейс, нет необходимости ограничивать ссылочный тип в определенных методах.
ICounter
интерфейс является чрезмерным в этом примере. - Эта архитектура совершенно неверна с точки зрения принципов SOLID (тогда почему?).
2 ответа
ISP - это не классы и не объекты, а строго об интерфейсах, в частности, о дизайне интерфейсов. Этот принцип направлен на то, чтобы не поощрять группирование частично связанных методов в одном интерфейсе, чтобы пользователи не нуждались в подмножестве этих методов для реализации остальных как пустых функций (либо выбрасывая NotImplementedException
или оставить его буквально пустым { }
). Таким образом, проще реализовать более согласованные классы и более эффективно использовать интерфейсы.
В вашем примере вы комбинируете оба Counter
класс с ICounter
интерфейс, таким образом, который не имеет прямого отношения к концепции провайдера:
- Интернет-провайдер о реальных объектах. Вы не должны использовать "чрезмерные" ссылки на входящие параметры.
Это отчасти верно (если я правильно истолковываю понятие "чрезмерное"). Однако, как я уже говорил, ISP не о том, как взаимодействовать с реальными объектами, а о том, как определить полезный интерфейс.
- Интернет-провайдер о занятиях. Если ваш класс где-то уже использует полный интерфейс, нет необходимости ограничивать ссылочный тип в определенных методах. Интерфейс ICounter является чрезмерным в этом примере.
Это не правильно. Тот факт, что класс реализует интерфейс, ничего не значит, если вы создаете зависимость от конкретного класса, а не от интерфейса. Помните, что интерфейс обеспечивает разделение различных частей вашей программы, заставляя компоненты полагаться на контракты, а не на конкретную реализацию, которая может измениться в будущем. Используя конкретный класс, вы теряете это преимущество. И опять же, это не совсем связано с концепцией интернет-провайдера.
- Эта архитектура совершенно неверна с точки зрения принципов SOLID (тогда почему?).
С архитектурной точки зрения с акцентом на принципы SOLID, я бы предложил иметь зависимость от ICounter
скорее, чем Counter
и в том числе IncrementAndSend
как часть определения интерфейса.
Я думаю, что было бы лучше добавить метод IncrementAndSend к интерфейсу и сделать класс OneToManyRouter зависимым от IEnumerable