Паттерн Фабричный метод — продвинутая версия

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

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

Пример

Допустим, у нас есть Фабрика, который будет создавать CarEngine на основе модели Car. Чаще всего я вижу такую реализацию:

class CarEngineFactory implements ICarEngineFactory
{
    public function make(string $carBrand): IEngine
    {
       switch ($carBrand) {
           case "mercedes":
              return new MercedesEngine();
           case "bmw":
               return new BmwEngine();
           default: 
                throw new CarEngineNotAvailableException();          
       }
    }
}

Выглядит вроде нормально, да? Ну, пока у нас есть только два варианта, то да. Но если мы будем добавлять все больше и больше марок автомобилей в нашу систему, этот фабрикой станет всё сложнее пользоваться.

Проблемы:

  1. Параметры — это просто случайные строки, поэтому разработчикам будет сложно разобраться, что нужно передавать.
  2. Чем больше марок автомобилей добавлено — тем больше будет блок switch.

Я вижу решение этого таким:

class CarEngineFactory implements ICarEngineFactory
{
    protected static $availableEngines = [
        Mercedes::class => MercedesEngine::class,
        Bmw::class => BmwEngine::class,
    ];

    public function make(ICar $car): IEngine
    {
        $carClass = get_class($car);

        if (!array_key_exists(get_class($car), self::$availableEngines)) {
            throw new CarEngineNotAvailableException();
        }

        $engineClass = self::$availableEngines[$carClass];

        return (new $engineClass());
    }
}

Теперь у нас есть статический массив с классами Car и CarEngine. Вместо рандомных строк мы передаем весь объект. И теперь, если мы хотим добавить еще одну марку Car с соответствующим CarEngine, то нам просто нужно описать это в $availableEngines, и готово!

Если у нас очень большой блок switch case, то использование array_key_exists будет намного быстрее, потому что эта команда выполняет поиск по хешу и работает почти во временной сложности O(1) (почти — из-за возможных коллизий хешей).

Этот метод предоставляем нам больше гибкости. Например, $availableEngines можно переместить в конфигурационный файл.

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

Автор: Georgi Razsolkov
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.