Статьи

Строители и Манипуляторы

Вот простой принцип именования методов в ООП, которому я пытаюсь следовать в своем коде: это глагол, если он манипулирует , это существительное, если он строит . Вот и все. Ничего промежуточного. Такие методы, как saveFile() или getTitle() не подходят и должны быть переименованы и реорганизованы. Более того, методы, которые «манипулируют», всегда должны возвращать void , например print() или save() . Позволь мне объяснить.

Во-первых, я должен сказать, что эта идея очень похожа на идею, предложенную Бертраном Мейером в его книге «Построение объектно-ориентированного программного обеспечения», где он предлагает разделить методы объекта на две четко разделенные категории: запросы и команды.

Идея этого принципа довольно философская. Давайте начнем со строителей, которые должны создать или найти объект, а затем вернуть его. Предположим, у меня есть магазин книг, и я прошу его дать мне книгу по имени:

1
2
3
interface Bookshelf {
  Book find(String title);
}

Это, очевидно, «строитель» (или «запрос» в терминах Мейера). Я прошу книгу, и она мне дана. Проблема, однако, заключается в названии метода. Это называется «найти», что подразумевает, что я знаю, как будет рассматриваться книга. Это будет найдено.

Однако это не то, как мы должны относиться к нашим объектам. Мы не должны говорить им, как делать работу, которую мы хотим, чтобы они делали. Вместо этого мы должны позволить им решить, будет ли книга найдена, построена или может быть взята из кеша памяти. Когда мы запрашиваем, мы должны сказать, какой результат мы ищем, и позволить объекту принять решение о том, как этот результат будет построен. Гораздо более подходящее имя для этого метода будет book() :

1
2
3
interface Bookshelf {
  Book book(String title);
}

Основное правило: строитель всегда существительное. Если метод возвращает что-то, это должно быть существительное. Желательно, чтобы его имя объясняло, что возвращает метод. Если это книга, назовите ее book() . Если это файл, вызовите метод file() и т. Д. Вот несколько хороших примеров компоновщика:

1
2
3
4
5
6
interface Foo {
  float speed(Actor actor);
  Money salary(User user);
  File database();
  Date deadline(Project project, User user);
}

Вот, наоборот, несколько примеров плохо названных строителей:

1
2
3
4
5
6
interface Foo {
  float calculateSpeed(Actor actor);
  Money getSalary(User user);
  File openDatabase();
  Date readDeadline(Project project, User user);
}

Там нет места для глагола в имени строителя!

Кстати, дело не только в названии. Конструктор, поскольку его имя не содержит глагола, не должен вносить никаких изменений в инкапсулированные объекты. Он может только создать или найти что-то и вернуть это. Как и чистая функция , она не должна иметь побочных эффектов.

Далее идут «манипуляторы» (или «команды» в терминах Мейера). Они делают некоторую работу для нас, изменяя сущности, которые инкапсулирует объект. Они противоположны строителям, потому что они действительно вносят изменения в мир, абстрагированный объектом. Например, мы просим Bookshelf добавить новую книгу к себе:

1
2
3
interface Bookshelf {
  void add(Book book);
}

Метод добавляет книгу в хранилище. Как именно будет изменено хранилище, мы не знаем. Но мы знаем, что поскольку имя метода является глаголом, будут изменения.

Также манипуляторы не должны ничего возвращать. Всегда void что мы рассматриваем как тип их ответа. Это необходимо в основном для того, чтобы отделить императивную часть кода от декларативной части. Мы либо получаем предметы, либо говорим им, что делать. Мы не должны смешивать эти действия в одном методе.

Цель этих правил — сделать код проще. Если вы будете следовать им, и все ваши строители только возвращают объекты, а ваши манипуляторы только изменяют мир, весь дизайн станет более понятным. Методы будут меньше, а имена короче.

Конечно, очень часто вам будет трудно найти эти имена. Время от времени вы захотите вернуть что-нибудь из манипулятора или сделать так, чтобы ваш конструктор внес некоторые изменения, скажем, в кеш. Попытайтесь противостоять этому искушению и придерживайтесь принципа: метод — это либо строитель, либо манипулятор, ничто посередине. Приведенные выше примеры довольно примитивны, код в реальной жизни намного сложнее. Но в этом нам поможет этот принцип — упрощение кода.

Мне также известен принцип существительного / глагола , который предлагает всегда называть классы существительными, а их методы — глаголами. Я считаю, что это неправильная идея, поскольку она не отличает строителей от манипуляторов и побуждает нас всегда мыслить с точки зрения императивных инструкций. Я считаю, что ООП должно быть гораздо больше о декларативной композиции объектов, даже если нам иногда приходится получать их от других объектов, а не создавать их экземпляры с помощью конструкторов. Вот почему нам нужны строители в большинстве ситуаций, и мы также должны видеть очевидную разницу между ними и другими методами, манипуляторами.

Опубликовано на Java Code Geeks с разрешения Егора Бугаенко, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Строители и Манипуляторы

Мнения, высказанные участниками Java Code Geeks, являются их собственными.