Логотип ConstructiCat
CodeBust.
Навигация по разделу ▾

Раздутый интерфейс.

Раздутый интерфейс — антипаттерн, при котором один интерфейс обрастает таким числом операций, что каждый клиент зависит от методов, которые не вызывает.

Раздутый интерфейс (англ. 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; текст переведён и отредактирован, добавлены примеры.