---
title: "Сравнение фабрик"
type: "design-pattern"
slug: "factory-comparison"
url: "http://localhost:3000/ru/design-patterns/factory-comparison.md"
---
# Сравнение фабрик

Эта статья покажет разницу между:

1. Фабрикой
2. Создающим методом
3. Статическим создающим (или фабричным) методом
4. Простой фабрикой
5. Паттерном [Фабричный метод](/ru/design-patterns/factory-method)
6. Паттерном [Абстрактная фабрика](/ru/design-patterns/abstract-factory)

Ссылки на эти термины можно встретить по всему интернету. Хотя они выглядят похоже, у них совершенно разные значения. Многие этого не осознают, что приводит к путанице и недопониманию.

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

## 1\. Фабрика

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

Например, любую из этих вещей можно вскользь назвать «фабрикой»:

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

Обычно, когда кто-то произносит слово «фабрика», точное значение должно быть понятно из контекста. Но если вы сомневаетесь, просто спросите. Не исключено, что автор сам не разбирается в вопросе.

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

**Создающий метод** определён в книге [Refactoring To Patterns](https://refactoring.guru/ref-to-patterns-book) как «метод, создающий объекты». Это означает, что каждый результат паттерна «фабричный метод» является «создающим методом», но не наоборот. Это также означает, что вы можете подставлять термин «создающий метод» везде, где Мартин Фаулер использует термин «фабричный метод» в книге [Рефакторинг](https://refactoring.guru/ref-book), и везде, где Джошуа Блох использует термин «статический фабричный метод» в книге [Effective Java](https://refactoring.guru/effective-java-book).

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

Многие назвали бы такие методы «фабричным методом» только потому, что они производят новые объекты: логика проста — метод создаёт объекты, а раз все _фабрики_ создают объекты, то этот метод явно должен быть _фабричным методом_. Естественно, возникает масса путаницы, когда речь заходит о настоящем паттерне [Фабричный метод](/ru/design-patterns/factory-method).

В следующем примере `next` — это создающий метод:

class Number {
    private $value;

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

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

## 3\. Статический создающий метод

**Статический создающий метод** — это создающий метод, объявленный как `static`. Иными словами, его можно вызвать у класса, и для этого не требуется создавать объект.

Не сбивайтесь с толку, когда кто-то называет такие методы «статическим фабричным методом». Это просто дурная привычка. [Фабричный метод](/ru/design-patterns/factory-method) — это паттерн проектирования, основанный на наследовании. Если сделать его `static`, вы больше не сможете переопределять его в подклассах, что сводит на нет смысл паттерна.

Когда статический создающий метод возвращает новые объекты, он становится альтернативным конструктором.

Это может быть полезно, когда:

* Вам нужно иметь несколько разных конструкторов с разным назначением, но их сигнатуры совпадают. Например, иметь одновременно и `Random(int max)`, и `Random(int min)` невозможно в Java, C++, C# и многих других языках. И самый популярный обходной путь — создать несколько статических методов, которые вызывают конструктор по умолчанию, а затем задают подходящие значения.
* Вы хотите повторно использовать существующие объекты вместо создания новых (см. паттерн [Одиночка](/ru/design-patterns/singleton)). Конструкторы в большинстве языков программирования обязаны возвращать новые экземпляры класса. Статический создающий метод — обходной путь для этого ограничения. Внутри статического метода ваш код может решить, создавать ли новый экземпляр, вызвав конструктор, или вернуть существующий объект из какого-нибудь кэша.

В следующем примере метод `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\. Паттерн _Простая фабрика_

Паттерн **Простая фабрика** Определён в книге [Head First. Паттерны проектирования](https://refactoring.guru/head-first-book). описывает класс, у которого есть один создающий метод с большим условным оператором, выбирающим на основе параметров метода, какой класс продукта инстанцировать и затем вернуть.

Люди обычно путают _простые фабрики_ с фабриками вообще или с одним из порождающих паттернов проектирования. В большинстве случаев простая фабрика — это промежуточный шаг к внедрению паттернов [Фабричный метод](/ru/design-patterns/factory-method) или [Абстрактная фабрика](/ru/design-patterns/abstract-factory).

Простая фабрика обычно представлена единственным методом в единственном классе. Со временем этот метод может стать слишком большим, поэтому вы можете решить вынести части метода в подклассы. Сделав это несколько раз, вы можете обнаружить, что всё это превратилось в классический паттерн _фабричный метод_.

Кстати, если объявить простую фабрику `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\. Паттерн _Фабричный метод_

**Фабричный метод** Определён в книге GoF [Приёмы объектно-ориентированного проектирования. Паттерны проектирования](https://refactoring.guru/gof-book). — это порождающий паттерн проектирования, который предоставляет интерфейс для создания объектов, но позволяет подклассам изменять тип создаваемого объекта.

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

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\. Паттерн _Абстрактная фабрика_

**Абстрактная фабрика** Также определён в [книге GoF](https://refactoring.guru/gof-book). — это порождающий паттерн проектирования, который позволяет создавать семейства связанных или зависимых объектов, не указывая их конкретных классов.

Что такое «семейства объектов»? Например, возьмём такой набор классов: `Transport` \+ `Engine` \+ `Controls`. Может быть несколько вариантов:

1. `Car` \+ `CombustionEngine` \+ `SteeringWheel`
2. `Plane` \+ `JetEngine` \+ `Yoke`

Если ваша программа не оперирует семействами продуктов, то абстрактная фабрика вам не нужна.

И снова: многие путают паттерн _абстрактная фабрика_ с классом простой фабрики, объявленным как `abstract`. Не делайте так!

### Послесловие

Теперь, когда вы знаете разницу, взгляните по-новому на паттерны проектирования:

* [Фабричный метод](/ru/design-patterns/factory-method)
* [Абстрактная фабрика](/ru/design-patterns/abstract-factory)
