Конструкторы vs Factory Методы

При моделировании классов, что является предпочтительным способом инициализации:

  • Конструкторы или
  • Factory Методы

И каковы были бы соображения для использования любого из них?

В некоторых ситуациях я предпочитаю иметь метод factory, который возвращает null, если объект не может быть сконструирован. Это делает код аккуратным. Я могу просто проверить, не возвращает ли возвращаемое значение значение null, прежде чем принимать альтернативные действия, в отличие от того, чтобы исключить из конструктора исключение. (Мне лично не нравятся исключения)

Скажем, у меня есть конструктор класса, который ожидает значение id. Конструктор использует это значение для заполнения класса из базы данных. В случае, когда запись с указанным идентификатором не существует, конструктор генерирует исключение RecordNotFoundException. В этом случае мне придется заключить конструкцию всех таких классов в блок try..catch.

В отличие от этого, у меня может быть статический метод factory для тех классов, которые возвратят значение null, если запись не будет найдена.

Какой подход лучше в этом случае, конструктор или метод factory?

Ответ 1

Со страницы 108 Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения от Gamma, Helm, Johnson и Vlissides.

Используйте шаблон метода Factory, когда

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

Ответ 2

Спросите себя, кто они и почему у нас есть. Они оба существуют, чтобы создать экземпляр объекта.

ElementarySchool school = new ElementarySchool();
ElementarySchool school = SchoolFactory.Construct(); // new ElementarySchool() inside

Пока нет разницы. Теперь представьте, что у нас есть разные типы школ, и мы хотим перейти от использования ElementarySchool к HighSchool (который получен из ElementarySchool или реализует тот же интерфейс ISchool, что и ElementarySchool). Изменение кода было бы следующим:

HighSchool school = new HighSchool();
HighSchool school = SchoolFactory.Construct(); // new HighSchool() inside

В случае интерфейса у нас будет:

ISchool school = new HighSchool();
ISchool school = SchoolFactory.Construct(); // new HighSchool() inside

Теперь, если у вас есть этот код в нескольких местах, вы можете увидеть, что использование метода factory может быть довольно дешевым, поскольку после изменения метода factory вы выполняете (если мы используем второй пример с интерфейсами).

И это основное различие и преимущество. Когда вы начинаете работать с сложными иерархиями классов и хотите динамически создавать экземпляр класса из такой иерархии, вы получаете следующий код. factory методы могут затем принять параметр, который сообщает методу, что конкретный экземпляр для создания экземпляра. Скажем, у вас есть класс MyStudent, и вам нужно создать экземпляр соответствующего объекта ISchool, чтобы ваш ученик был членом этой школы.

ISchool school = SchoolFactory.ConstructForStudent(myStudent);

Теперь у вас есть одно место в вашем приложении, которое содержит бизнес-логику, которая определяет, какой объект ISchool создает экземпляр для разных объектов IStudent.

Итак - для простых классов (объекты значений и т.д.) конструктор просто прекрасен (вы не хотите переопределять ваше приложение), но для сложных классов иерархии метод factory является предпочтительным способом.

Таким образом, вы следуете первому принципу дизайна из группы из четырех книг "Программа для интерфейса, а не реализация".

Ответ 3

Вам нужно прочитать (если у вас есть доступ) Эффективный Java 2 Пункт 1: Рассмотрим статические методы factory вместо конструкторы.

Статические преимущества factory:

  • У них есть имена.
  • Они не обязаны создавать новый объект при каждом вызове.
  • Они могут возвращать объект любого подтипа возвращаемого типа.
  • Они уменьшают многословность создания экземпляров с параметризованным типом.

Статические factory недостатки методов:

  • При предоставлении только статических factory методов классы без общедоступных или защищенных конструкторов нельзя подклассы.
  • Они не могут быть легко различимы с другими статическими методами.

Ответ 4

По умолчанию конструкторы должны быть предпочтительнее, потому что их проще понять и написать. Однако, если вам необходимо отделить конструктивные тонкости объекта от его смыслового значения, понимаемого клиентским кодом, вам лучше использовать фабрики.

Разница между конструкторами и фабриками аналогична, например, переменной и указателю на переменную. Там другой уровень косвенности, что является недостатком; но есть и другой уровень гибкости, что является преимуществом. Поэтому, делая выбор, вам будет рекомендовано провести анализ затрат и выгод.

Ответ 5

Приведите ссылку "Эффективная Java", 2-е изд., пункт 1: рассмотрите статические методы factory вместо конструкторов, p. 5:

"Обратите внимание, что статический factory метод не совпадает с шаблоном метода factory от шаблонов проектирования [Gamma95, p. 107]. Статический метод factory, описанный в этот элемент не имеет прямого эквивалента в шаблонах проектирования. "

Ответ 6

Используйте factory только тогда, когда вам необходим дополнительный контроль с созданием объекта, что невозможно было бы сделать с конструкторами.

Заводы имеют возможность кэширования, например.

Другой способ использования заводов - это сценарий, в котором вы не знаете тип, который хотите построить. Часто вы видите этот тип использования в сценариях plugin factory, где каждый плагин должен выводиться из базового класса или реализовывать какой-то интерфейс. factory создает экземпляры классов, которые выводятся из базового класса или реализуют интерфейс.

Ответ 7

Конкретный пример из приложения CAD/CAM.

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

Форма будет создаваться с помощью factory. Потому что, хотя есть класс формы, каждая фигура будет настраиваться по-разному в зависимости от того, какой тип формы он имеет. Мы не знаем, какую форму мы собираемся инициализировать, пока пользователь не сделает выбор.

Ответ 8

Скажем, у меня есть конструктор класса, который ожидает значение id. Конструктор использует это значение для заполнения класса из базы данных.

Этот процесс должен определенно находиться вне конструктора.

  • Конструктор не должен обращаться к базе данных.

  • Задача и причина для конструктора - инициализировать члены данных и установить инвариант класса, используя значения, переданные в конструктор.

  • Для всего остального лучше использовать статический метод factory или в более сложных случаях отдельный класс factory или строителя.

Некоторые строки руководства конструктора из Microsoft:

Выполняйте минимальную работу в конструкторе. Конструкторы не должны много работать, кроме как захватить параметры конструктора. Стоимость любой другой обработки должна быть отложена до тех пор, пока не потребуется.

и

Рассмотрите возможность использования статического метода factory вместо конструктора, если семантика нужной операции не отображается непосредственно в конструкцию нового экземпляра.