Сравнение фабрик

В этой статье мы попробуем разобраться чем отличаются:

Во многих книгах и источниках определения «фабрик» даётся авторами по-своему. Это создаёт большую путаницу при чтении материалов в интернете.

Итак, давайте разберёмся в вариациях фабрик, чтобы раз и навсегда понять разницу между ними.

#1. Фабрика

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

Вы можете услышать слово Фабрика от других людей, когда они имеют в виду:

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

То, что человек имеет в виду, произнося Фабрика, проще всего понять из контекста, мы сейчас рассмотрим все вариации.

#2. Создающий метод

Создающий метод — это простой метод-обёртка над вызовом конструктора продукта. Выделив создающий метод, вы изолируете любые изменения в конструировании продуктов от основного кода. Например, вы можете вовсе убрать вызов конструктора из создающего метода, отдавая вместо нового какой-то существующий объект.

Многие называют его фабричным методом, только потому, что он создаёт новые объекты. Типичная логика: «этот метод создаёт объекты, а раз все фабрики создают что-то, значит этот метод — фабричный». И это вносит основную путаницу между понятием Создающего метода и паттерном Фабричный метод.

В этом примере, метод next является создающим методом:

class Number {
    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public function next() {
        return new Number ($this->value + 1);
    }
}

#3. Статический фабричный метод

Статический фабричный метод — вариация создающего метода, объявленная как static. Если этот метод создаёт объекты своего же класса, то, по сути, он выступает в роли альтернативного конструктора. Это может быть полезно, если:

  • Требуется создать разные по функциональности конструкторы, у которых бы совпадали сигнатуры (например, Random(int max) и Random(int min)). Это невозможно во многих языках программирования, но создав статический метод, вы можете обойти это ограничение.

  • Хочется повторно использовать готовые объекты, вместо создания новых (например, паттерн Одиночка). При вызове конструктора вы всегда создаёте новый объект. Это можно обойти, если вынести вызов конструктора в новый метод. В этом методе вы можете сначала поискать готовый объект в каком-то кеше, и только если его нет, создать новый объект.

В следующем примере, метод load является статическим фабричным методом – он предоставляет удобный способ загрузить пользователя из базы данных.

class User {
    private $name, $email, $phone;

    public function __construct($name, $email, $phone) {
        $this->name = $name;
        $this->email = $email;
        $this->phone = $phone;
    }

    public static function load($id) {
        list($name, $email, $phone) = DB::load_data('users', 'name', 'email', 'phone');
        $user = new User($name, $email, $phone);
        return $user;
    }
}

#4. Паттерн Простая фабрика

Паттерн Простая фабрика — это класс, в котором есть один метод с большим условным оператором, выбирающим создаваемый продукт. Этот метод вызывают с неким параметром, по которому определяется какой из продуктов нужно создать. У простой фабрики, обычно, нет подклассов.

Обычно, простую фабрику путают с общим понятием Фабрики или с любым из фабричных паттернов.

Если объявить класс простой фабрики абстрактным (Java, C#), это не сделает его одним и тем же, что и абстрактная фабрика!

Вот пример простой фабрики:

class UserFactory {
    public static function create($type) {
        switch ($type) {
            'user': return new User();
            'customer': return new Customer();
            'admin': return new Admin();
            default:
                throw new Exception('Wrong user type passed.');
        }
    }
}

Простая фабрика находится в шаге от того, чтобы стать Фабричным методом.

#5. Паттерн Фабричный метод

Паттерн Фабричный метод — это устройство классов, при котором подклассы могут переопределять тип создаваемого в суперклассе продукта.

Если вы имеете иерархию продуктов и абстрактный создающий метод, который переопределяется в подклассах, то перед вами паттерн Фабричный метод.

class Department {
    public abstract function createEmployee($id);

    public function fire($id) {
        $employee = $this->createEmployee($id);
        $employee->paySallary();
        $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. Паттерн Абстрактная фабрика

Паттерн Абстрактная фабрика — это устройство классов, облегчающее создание семейств продуктов.

Что такое семейство продуктов? Например, классы Транспорт + Двигатель + Управление. Вариациями этого семейства могут стать:

  1. Автомобиль + ДвигательВнутренннегоСгорания + Руль
  2. Самолет + РеактивныйДвигатель + Штурвал

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

Многие путают паттерн абстрактная фабрика с классом простой фабрики, объявленным как abstract, но это далеко не одно и то же!

Послесловие

Теперь, когда вы окончательно разобрались в терминологии, почитайте наши описания фабричных паттернов: