Ответ
Принцип разделения интерфейсов (ISP) утверждает, что клиенты не должны зависеть от методов, которые они не используют. В Dart это реализуется через создание небольших, специфичных интерфейсов (абстрактных классов), а не одного громоздкого.
Пример нарушения ISP:
// Один интерфейс для всех сущностей, заставляющий реализовывать ненужные методы.
abstract class Worker {
void code();
void test();
void deploy();
void cook();
}
class Developer implements Worker {
@override
void code() => print('Writing Flutter app');
@override
void test() => print('Running unit tests');
@override
void deploy() => print('Deploying to Firebase');
@override
void cook() => throw UnimplementedError('Developer cannot cook!'); // Ненужный метод!
}
Пример следования ISP:
// Разделяем на логичные, узкие интерфейсы.
abstract class Codeable {
void code();
}
abstract class Testable {
void test();
}
abstract class Deployable {
void deploy();
}
abstract class Cookable {
void cook();
}
// Классы реализуют только нужные им интерфейсы.
class FlutterDeveloper implements Codeable, Testable, Deployable {
@override
void code() => print('Writing Flutter app with Dart');
@override
void test() => print('Running widget tests');
@override
void deploy() => print('Building APK/IPA and deploying to store');
}
class Chef implements Cookable {
@override
void cook() => print('Cooking dinner');
}
Преимущества:
- Избегаем «жирных» интерфейсов. Классы не загрязнены нерелевантными методами.
- Повышаем стабильность. Изменения в одном интерфейсе (например,
Deployable) затрагивают только классы, которые от него зависят, а не всех «работников». - Упрощаем тестирование и понимание. Легче мокать и тестировать небольшие контракты.
Ответ 18+ 🔞
А, ну это тот самый принцип, когда тебе впаривают один здоровенный интерфейс, а ты потом сидишь и думаешь: «Нахуя мне этот метод, я его никогда не вызываю?». Это как если бы тебе в машину встроили швейную машинку. Ну, типа, вдруг пригодится? А на деле она только место занимает и ты про неё вечно спотыкаешься.
Вот смотри, какой пиздец может получиться, если не следовать этому принципу.
// Один интерфейс для всех сущностей, заставляющий реализовывать ненужные методы.
abstract class Worker {
void code();
void test();
void deploy();
void cook();
}
class Developer implements Worker {
@override
void code() => print('Writing Flutter app');
@override
void test() => print('Running unit tests');
@override
void deploy() => print('Deploying to Firebase');
@override
void cook() => throw UnimplementedError('Developer cannot cook!'); // Ненужный метод!
}
Представляешь? Разработчик, который должен готовить. Это ж ёперный театр! Он либо сгорит на кухне, либо выбросит исключение, как у нас в коде. И каждый раз, когда ты создаёшь нового Developer, ты должен про эту проклятую готовку помнить. Доверия к такому интерфейсу — ноль ебать. Подозрение ебать чувствую, что его писал какой-нибудь архитектор, который в жизни код не писал.
А теперь правильный подход, где мы не пытаемся из каждого сделать универсального солдата.
// Разделяем на логичные, узкие интерфейсы.
abstract class Codeable {
void code();
}
abstract class Testable {
void test();
}
abstract class Deployable {
void deploy();
}
abstract class Cookable {
void cook();
}
// Классы реализуют только нужные им интерфейсы.
class FlutterDeveloper implements Codeable, Testable, Deployable {
@override
void code() => print('Writing Flutter app with Dart');
@override
void test() => print('Running widget tests');
@override
void deploy() => print('Building APK/IPA and deploying to store');
}
class Chef implements Cookable {
@override
void cook() => print('Cooking dinner');
}
Вот теперь красота! FlutterDeveloper — он Codeable, Testable и Deployable. И всё. Он не Cookable. Он не должен быть. Пусть шеф готовит, у него это лучше получается. Каждый занимается своим делом, интерфейсы маленькие и понятные.
И какие плюсы-то, спросишь?
- Жирных интерфейсов нет. Класс не превращается в свалку ненужных методов, которые только путают. Никаких
UnimplementedErrorиз-за того, что тебя заставили готовить. - Стабильность выше крыши. Захотел поменять что-то в процессе деплоя? Меняешь интерфейс
Deployableи всё. Тебя не ебут все подряд классы в проекте, а только те, кто реально деплоит. Никаких сюрпризов. - Тестировать и понимать — одно удовольствие. Представь, нужно замокать объект для теста. Если у него методов овердохуища, это ад. А если три маленьких интерфейса — да похуй, сделал мок для
Codeableи спи спокойно. Код читается как книга, потому что сразу видно, что умеет твой класс, а что нет.
Короче, смысл в том, чтобы не создавать этих монстров на все случаи жизни. Лучше десять маленьких и точных интерфейсов, чем один большой, от которого всем тошно.