Ответ
I в SOLID — это Interface Segregation Principle (ISP), Принцип разделения интерфейсов.
Формулировка: Клиенты не должны зависеть от методов, которые они не используют. Многочисленные специализированные интерфейсы лучше, чем один универсальный.
Проблема — "толстый" интерфейс:
// Нарушение ISP. Принтер вынужден реализовывать метод Scan, который ему не нужен.
public interface IMultiFunctionDevice
{
void Print(Document d);
void Scan(Document d);
void Fax(Document d);
}
public class OldFashionedPrinter : IMultiFunctionDevice
{
public void Print(Document d) { /* OK */ }
public void Scan(Document d) { throw new NotImplementedException(); } // Ненужный метод!
public void Fax(Document d) { throw new NotImplementedException(); } // Ненужный метод!
}
Решение — разделение интерфейсов:
// Создаем несколько узкоспециализированных интерфейсов.
public interface IPrinter { void Print(Document d); }
public interface IScanner { void Scan(Document d); }
public interface IFax { void Fax(Document d); }
// Класс реализует только то, что ему действительно нужно.
public class OldFashionedPrinter : IPrinter
{
public void Print(Document d) { /* Реализация */ }
}
// Современное устройство может реализовать несколько интерфейсов.
public class Photocopier : IPrinter, IScanner
{
public void Print(Document d) { /* ... */ }
public void Scan(Document d) { /* ... */ }
}
Преимущества ISP:
- Уменьшение связанности: Классы перестают зависеть от неиспользуемых методов.
- Повышение стабильности: Изменения в одном интерфейсе (например,
IFax) не затрагивают классы, которые его не используют. - Улучшение читаемости и тестируемости: Интерфейсы становятся более четкими и целенаправленными.
Ответ 18+ 🔞
Слушай, а вот этот принцип «I» — это вообще про то, чтобы не делать из интерфейса швейцарский нож на все случаи жизни. По-русски он звучит как «Принцип разделения интерфейсов», но суть проще: не заставляй класс реализовывать то, что ему нахуй не сдалось.
Вот смотри, представь, что ты проектируешь устройство. Берёшь и пишешь один жирный интерфейс на всё подряд:
public interface IMultiFunctionDevice
{
void Print(Document d);
void Scan(Document d);
void Fax(Document d);
}
И тут ты пытаешься под него сделать старый дедовский принтер, который только печатает. И что получается? Он вынужден реализовывать Scan и Fax! А он нихуя не умеет сканировать и факсить! Приходится либо пустые методы оставлять, либо вообще throw new NotImplementedException() кидать. Это пиздец как неудобно и криво. Класс начинает зависеть от кучи чужих обязательств, которые ему вообще не нужны.
А правильный подход какой? Дробить этот жирный интерфейс на мелкие, узкоспециализированные куски!
public interface IPrinter { void Print(Document d); }
public interface IScanner { void Scan(Document d); }
public interface IFax { void Fax(Document d); }
Теперь твой старый принтер спокойно реализует только IPrinter и ни о чём не парится:
public class OldFashionedPrinter : IPrinter
{
public void Print(Document d) { /* Реализация */ }
}
А если у тебя есть современная копировалка, которая и печатает, и сканирует — ну так реализуй оба интерфейса и не мучайся:
public class Photocopier : IPrinter, IScanner
{
public void Print(Document d) { /* ... */ }
public void Scan(Document d) { /* ... */ }
}
В чём профит?
- Связанность падает. Классы перестают тащить на себе груз ненужных методов.
- Стабильность растёт. Захотел поменять что-то в логике факса — ты трогаешь только
IFaxи классы, которые его используют. Старые принтеры даже не узнают об этом, им похуй. - Код становится чище. Маленькие интерфейсы проще читать, тестировать и понимать. Сразу видно, за что класс отвечает, а за что — нет.
Короче, смысл в том, чтобы не создавать монстров. Лучше десять маленьких и понятных интерфейсов, чем один большой, от которого всем тошно.