«Эффективная Java» (Джошуа Блох) — отличная книга для каждого Java-разработчика. В этом посте я подытожу содержание первого пункта книги, который касается рассмотрения статического фабричного метода вместо конструкторов.

Так что же это?

Обычно, когда мы создаем класс, мы предоставляем конструктор (или перегруженную версию) для создания экземпляра этого класса. Таким образом, пользователь этого класса может использовать этот конструктор для создания экземпляра. Это обычный способ. Вместо этого метода в книге предлагается использовать метод статической фабрики. Как и у любого другого решения, у этого метода есть как преимущества, так и недостатки. Во-первых, преимущества…

Преимущества!

  1. Читаемость. Статические фабричные методы могут иметь осмысленные имена, чем конструкторы. Это позволит пользователю узнать, что будет создано, если вы используете определенный фабричный метод. С конструкторами вы всегда должны использовать имя класса (перегружая конструктор, мы можем дифференцировать создание объекта), и пользователь должен помнить правильный конструктор перед его вызовом. Когда человек читает код с конструктором (или перегруженным конструктором), он может не знать, что делает этот конструктор, не проверив документацию класса. Если это статический фабричный метод со значимым именем, пользователь этого метода знает. Например: Collections.synchronizedMap, Optional.of.
  2. Управление созданием экземпляра. В отличие от конструкторов, статические фабричные методы могут управлять созданием объекта. Это позволит создавать неизменяемые классы, внедрять кеширование и экономить ресурсы, а также повышать производительность. Для этого можно использовать техники, связанные с паттерном облегченного веса.
  3. Может возвращать объект любого подтипа: это обеспечит большую гибкость при разработке API, где он должен быть гибким для изменений, но скрыть сложность от пользователя класса (например, разработчика). Класс/API может возвращать объекты, не делая их классы общедоступными. Пример: фабричный метод Collections.synchronizedMap. Как разработчики, мы верим, что этот метод возвращает синхронизированную карту, поставщики JDK должны будут иметь полную гибкость, чтобы обеспечить правильную реализацию, и, возможно, поставщик JDK может изменить реализацию с помощью новой версии JDK. Это 4-й пункт ниже.
  4. Класс может изменять возвращаемый объект от вызова к вызову: допустим, у нас есть фабричный метод для возврата объекта типа A, и с текущей реализацией мы возвращаем объект класса B, где класс B также реализует тип А (версия 1). В новом выпуске разработчики API могут возвращать совершенно новый объект класса C, где класс C также реализует тип A (версия 2). Класс C — это совершенно новый класс, даже если его не было, когда была выпущена версия 1.

Недостатки!

  1. Подклассы: изначально, если конструктор необходимо пометить как закрытый (чтобы сделать его неизменяемым или по какой-либо другой причине), этот класс не может быть подклассом. Это может повлиять, если вам нужна поддержка подклассов (некоторые фреймворки зависят от подклассов).
  2. Трудно найти: на данный момент Java документирует только отдельные конструкторы, методы и поля. Специального раздела для этих фабричных методов нет. Возможно, в будущем у него может быть и статический раздел factory :)

Заключение. Это хорошая практика проектирования API, позволяющая иметь гибкий, расширяемый API с хорошей удобочитаемостью для пользователя.