Внутренний и внешний завод

Я обдумываю один из двух разных способов реализации фабричного шаблона в PHP. Я не знаю, есть ли у этих вариантов собственные имена, поэтому сейчас я буду называть их внутренним заводом и внешним заводом.

Внутренняя фабрика: фабричный метод реализован в самом классе как статический общедоступный метод.

<?php
class Foo
{
    protected
        $loadedProps = false;

    public static factory ($id)
    {
        $class = get_called_class ();
        $item = new $class ($id);
        if ($item -> loadedProps ())
        {
            return ($item);
        }
    }

    public function loadedProps ()
    {
        return ($this -> loadedProps);
    }

    protected function loadPropsFromDB ($id)
    {
        // Some SQL logic goes here
    }

    protected function __construct ($id)
    {
        $this -> loadedProps = $this -> loadPropsFromDB ($id);
    }
}
?>

Внешняя фабрика: фабрика и элементы, которые она инициализирует, реализованы как отдельные объекты.

<?php

class Foo
{
    protected
        $loadedProps = false;

    public function loadedProps ()
    {
        return ($this -> loadedProps);
    }

    protected function loadPropsFromDB ($id)
    {
        // Some SQL logic goes here
    }

    public function __construct ($id)
    {
        $this -> loadedProps = $this -> loadPropsFromDB ($id);
    }
}

abstract class FooFactory 
{
    public static factory ($id)
    {
        $item = new Foo ($id);
        if ($item -> loadedProps ())
        {
            return ($item);
        }
    }
}
?>

Теперь мне кажется, что у каждого есть свои достоинства.

Первый позволяет скрыть конструктор от внешнего мира. Это означает, что вы можете создать объект Foo только через фабрику. Если состояние элемента не может быть загружено из БД, тогда фабрика вернет NULL, что вы можете легко проверить в коде.

if ($item = Foo::factory ($id))
{
    // ...
}
else
{
    // The item failed to load.  Handle error here
}

Фабрика также может создавать объекты любого подкласса Foo без каких-либо изменений.

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

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

Однако у него есть свой набор недостатков. Прежде всего, конструктор элемента, который будет построен на фабрике, должен быть общедоступным, поскольку PHP не имеет концепции пакетов и уровня защиты «пакет» для членов класса. Это означает, что ничто не мешает кодеру просто выполнить new Foo () и обойти фабрику (хотя это может упростить модульное тестирование).

Другая проблема заключается в том, что FooFactory может создавать только объекты Foo, а не какие-либо его подклассы. Это можно обойти, добавив еще один параметр в FooFactory, чтобы указать имя класса, но тогда фабрика должна будет выполнить внутренние проверки того, что указанный объектный класс на самом деле является потомком Foo.

Итак, каковы относительные достоинства этих двух подходов и какой из них вы бы порекомендовали?

Кроме того, если у них больше имен собственных, чем у внутренней или внешней фабрики, я бы хотел их узнать.


person GordonM    schedule 14.04.2011    source источник


Ответы (1)


Фактически, фабрики - это установленные шаблоны творческого проектирования, поэтому вы можете прочитать об их назначении в книге GoF или на странице Sourcemaking:

  • Абстрактная фабрика - создает экземпляр нескольких семейств классов.
  • Строитель - отделяет конструкцию объекта от его представления.
  • Заводской метод - создает экземпляр нескольких производных классов.
  • Пул объектов. Избегайте дорогостоящего приобретения и высвобождения ресурсов за счет утилизации неиспользуемых объектов.
  • Прототип - полностью инициализированный экземпляр, который нужно скопировать или клонировать.

В этих шаблонах есть некоторое совпадение, особенно между Factory Method, Abstract Factory и Builder, и различие становится еще более размытым, когда вы создаете не семейства объектов, а только один тип объекта. Итак, да, для простоты предположим, что внутренние и внешние фабрики - правильные термины.

Лично я всегда предпочитаю внешнюю фабрику внутренней фабрике по причине, которую вы уже указали: я могу использовать Внедрение зависимостей и может разделить обязанности. Поскольку статические методы смертельны для тестируемости и могут быть опасным из-за связи, которую они создают, я бы сделал Factory реальным объектом, хотя вместо этого и используйте нестатические методы.

Два упомянутых вами недостатка на самом деле вовсе не являются недостатками.

Я не могу придумать причину, по которой я хотел бы запретить разработчику создавать экземпляры объектов, которые создает Factory. . Фактически, когда Unit-Testing я захочу создать этот объект самостоятельно и заменить все зависимости на Моки и заглушки. Я также не считаю, что разработчиков следует слишком много нянчить. Учитывая скриптовую природу PHP, изменить ctor с частного на публичный слишком легко, чтобы это в любом случае эффективно предотвратить.

Что касается другой проблемы, заключающейся в том, что Factory не может создавать другие классы, это не так. Идея Factory на самом деле состоит в создании различных типов семейства объектов. Даже фабричный метод явно позволяет создавать подклассы. Независимо от того, реализуете ли вы это с помощью переключателя / корпуса или с помощью различных методов на заводе, зависит от вас. Также нет причин не комбинировать Фабрики со Строителями или иметь Фабрики Фабрик, которые, в свою очередь, инкапсулируют логику для создания объекта. Таким образом устраняется необходимость в любой из упомянутых вами внутренних проверок (которые также могут быть выполнены с помощью подсказок).

Другой жизнеспособной альтернативой фабрикам было бы использование контейнера внедрения зависимостей, например Symfony Components DIC и управляйте своими объектами в основном через этот контейнер.

person Gordon    schedule 14.04.2011
comment
Off-topic: На случайной заметке я нахожу забавным, что вы оба говорите об одном и том же, когда у вас одно и то же имя пользователя. На секунду мне показалось, что вы говорите сами с собой. - person Tek; 15.04.2011
comment
@Tek да, Гордон отвечает Гордону. Но я Гордон ™ :) - person Gordon; 15.04.2011