Сравнение фабрик.
Эта статья покажет разницу между:
- Фабрикой
- Создающим методом
- Статическим создающим (или фабричным) методом
- Простой фабрикой
- Паттерном Фабричный метод
- Паттерном Абстрактная фабрика
Ссылки на эти термины можно встретить по всему интернету. Хотя они выглядят похоже, у них совершенно разные значения. Многие этого не осознают, что приводит к путанице и недопониманию.
Так что давайте попробуем разобраться в разнице и решить эту проблему раз и навсегда.
1. Фабрика
Фабрика — это многозначный термин, обозначающий функцию, метод или класс, которые должны что-то производить. Чаще всего фабрики производят объекты. Но они также могут создавать файлы, записи в базах данных и т. д.
Например, любую из этих вещей можно вскользь назвать «фабрикой»:
- функция или метод, который создаёт графический интерфейс программы;
- класс, который создаёт пользователей;
- статический метод, который определённым образом вызывает конструктор класса;
- один из порождающих паттернов проектирования.
Обычно, когда кто-то произносит слово «фабрика», точное значение должно быть понятно из контекста. Но если вы сомневаетесь, просто спросите. Не исключено, что автор сам не разбирается в вопросе.
2. Создающий метод
Создающий метод определён в книге Refactoring To Patterns как «метод, создающий объекты». Это означает, что каждый результат паттерна «фабричный метод» является «создающим методом», но не наоборот. Это также означает, что вы можете подставлять термин «создающий метод» везде, где Мартин Фаулер использует термин «фабричный метод» в книге Рефакторинг, и везде, где Джошуа Блох использует термин «статический фабричный метод» в книге Effective Java.
В действительности создающий метод — это всего лишь обёртка вокруг вызова конструктора. У него может быть просто имя, которое лучше выражает ваши намерения. С другой стороны, он может помочь изолировать ваш код от изменений в конструкторе. Он даже может содержать особую логику, которая возвращает существующие объекты вместо создания новых.
Многие назвали бы такие методы «фабричным методом» только потому, что они производят новые объекты: логика проста — метод создаёт объекты, а раз все фабрики создают объекты, то этот метод явно должен быть фабричным методом. Естественно, возникает масса путаницы, когда речь заходит о настоящем паттерне Фабричный метод.
В следующем примере next — это создающий метод:
class Number {
private $value;
public function __construct($value) {
$this->value = $value;
}
public function next() {
return new Number ($this->value + 1);
}
}
3. Статический создающий метод
Статический создающий метод — это создающий метод, объявленный как static. Иными словами, его можно вызвать у класса, и для этого не требуется создавать объект.
Не сбивайтесь с толку, когда кто-то называет такие методы «статическим фабричным методом». Это просто дурная привычка. Фабричный метод — это паттерн проектирования, основанный на наследовании. Если сделать его static, вы больше не сможете переопределять его в подклассах, что сводит на нет смысл паттерна.
Когда статический создающий метод возвращает новые объекты, он становится альтернативным конструктором.
Это может быть полезно, когда:
-
Вам нужно иметь несколько разных конструкторов с разным назначением, но их сигнатуры совпадают. Например, иметь одновременно и
Random(int max), иRandom(int min)невозможно в Java, C++, C# и многих других языках. И самый популярный обходной путь — создать несколько статических методов, которые вызывают конструктор по умолчанию, а затем задают подходящие значения. -
Вы хотите повторно использовать существующие объекты вместо создания новых (см. паттерн Одиночка). Конструкторы в большинстве языков программирования обязаны возвращать новые экземпляры класса. Статический создающий метод — обходной путь для этого ограничения. Внутри статического метода ваш код может решить, создавать ли новый экземпляр, вызвав конструктор, или вернуть существующий объект из какого-нибудь кэша.
В следующем примере метод load — это статический создающий метод. Он предоставляет удобный способ получать пользователей из базы данных.
class User {
private $id, $name, $email, $phone;
public function __construct($id, $name, $email, $phone) {
$this->id = $id;
$this->name = $name;
$this->email = $email;
$this->phone = $phone;
}
public static function load($id) {
list($id, $name, $email, $phone) = DB::load_data('users', 'id', 'name', 'email', 'phone');
$user = new User($id, $name, $email, $phone);
return $user;
}
}
4. Паттерн Простая фабрика
Паттерн Простая фабрика описывает класс, у которого есть один создающий метод с большим условным оператором, выбирающим на основе параметров метода, какой класс продукта инстанцировать и затем вернуть.
Люди обычно путают простые фабрики с фабриками вообще или с одним из порождающих паттернов проектирования. В большинстве случаев простая фабрика — это промежуточный шаг к внедрению паттернов Фабричный метод или Абстрактная фабрика.
Простая фабрика обычно представлена единственным методом в единственном классе. Со временем этот метод может стать слишком большим, поэтому вы можете решить вынести части метода в подклассы. Сделав это несколько раз, вы можете обнаружить, что всё это превратилось в классический паттерн фабричный метод.
Кстати, если объявить простую фабрику abstract, она не превратится волшебным образом в паттерн абстрактная фабрика.
Вот пример простой фабрики:
class UserFactory {
public static function create($type) {
switch ($type) {
case 'user': return new User();
case 'customer': return new Customer();
case 'admin': return new Admin();
default:
throw new Exception('Wrong user type passed.');
}
}
}
5. Паттерн Фабричный метод
Фабричный метод — это порождающий паттерн проектирования, который предоставляет интерфейс для создания объектов, но позволяет подклассам изменять тип создаваемого объекта.
Если у вас есть создающий метод в базовом классе и подклассы, которые его расширяют, возможно, перед вами фабричный метод.
abstract class Department {
public abstract function createEmployee($id);
public function fire($id) {
$employee = $this->createEmployee($id);
$employee->paySalary();
$employee->dismiss();
}
}
class ITDepartment extends Department {
public function createEmployee($id) {
return new Programmer($id);
}
}
class AccountingDepartment extends Department {
public function createEmployee($id) {
return new Accountant($id);
}
}
6. Паттерн Абстрактная фабрика
Абстрактная фабрика — это порождающий паттерн проектирования, который позволяет создавать семейства связанных или зависимых объектов, не указывая их конкретных классов.
Что такое «семейства объектов»? Например, возьмём такой набор классов: Transport + Engine + Controls. Может быть несколько вариантов:
Car+CombustionEngine+SteeringWheelPlane+JetEngine+Yoke
Если ваша программа не оперирует семействами продуктов, то абстрактная фабрика вам не нужна.
И снова: многие путают паттерн абстрактная фабрика с классом простой фабрики, объявленным как abstract. Не делайте так!
Послесловие
Теперь, когда вы знаете разницу, взгляните по-новому на паттерны проектирования: