Раздутый интерфейс.
Раздутый интерфейс — антипаттерн, при котором один интерфейс обрастает таким числом операций, что каждый клиент зависит от методов, которые не вызывает.
Раздутый интерфейс (англ. interface bloat) — антипаттерн, при котором один интерфейс — публичный API класса, интерфейс в Java или C#, экспортируемые функции модуля — накапливает гораздо больше операций, чем нужно любому из его клиентов.
Определение
Словом «раздувание» (bloat) называют разные проблемы проектирования ПО, в том числе пользовательские интерфейсы, перегруженные функциями и настройками. В проектировании API интерфейс считается раздутым, когда он объединяет за одним типом несколько несвязанных обязанностей: поиск, сохранение, администрирование, уведомления. Тогда каждая реализация вынуждена поддерживать их все, а каждый клиент зависит от операций, которые никогда не вызывает, поэтому изменение любой из них заставляет перекомпилировать, перетестировать или заново развёртывать посторонних клиентов.
Типичные признаки раздутого интерфейса:
- реализации оставляют методы пустыми или выбрасывают
UnsupportedOperationException; - тестовым заглушкам приходится реализовывать десяток методов ради теста, который использует один;
- группы методов используются разными клиентами и меняются по разным причинам.
Принцип разделения интерфейсов
Лекарство — принцип разделения интерфейсов (буква «I» в SOLID): клиенты не должны зависеть от методов, которые они не используют. Раздутый интерфейс разбивают на несколько небольших интерфейсов, каждый из которых описывает одну роль. Один класс может по-прежнему реализовывать их все, но каждый клиент видит только нужную ему роль.
Обычно начинают с рефакторинга Извлечение интерфейса. Раздутый интерфейс часто прикрывает божественный объект или большой класс, которые тоже приходится разделять.
Пример
Этот UserRepository смешивает поиск, сохранение, административные запросы и письма для сброса пароля. Страница профиля, которой нужен только byId, всё равно зависит от всех семи методов, и каждая подделка в её тестах обязана их реализовать.
interface UserRepository {
User byId(int id);
User byEmail(String email);
void save(User user);
void delete(int id);
void rename(int id, String name);
List<User> admins();
void sendReset(int id);
}
После разделения по ролям каждый клиент зависит только от того, что вызывает:
interface UserLookup {
User byId(int id);
User byEmail(String email);
List<User> admins();
}
interface UserStore {
void save(User user);
void delete(int id);
void rename(int id, String name);
}
interface PasswordReset {
void sendReset(int id);
}
class ProfilePage {
private final UserLookup users; // зависит только от того, что использует
ProfilePage(UserLookup users) { this.users = users; }
String title(int id) { return users.byId(id).name(); }
}
Один класс JdbcUsers по-прежнему может реализовывать и UserLookup, и UserStore — сократились только зависимости клиентов.
Адаптировано по статье Википедии “Interface bloat” (версия 1304469164), распространяется по лицензии CC BY-SA 4.0; текст переведён и отредактирован, добавлены примеры.