Статьи

Фабричные образцы: Карта Сотрудников

Контейнеры внедрения зависимостей часто предлагаются в качестве быстрого способа написания кода создания и связывания: создание экземпляров классов, соединение соавторов объектов и их повторное использование в графе объектов.

Однако, как и для каждой библиотеки, вы должны сначала оценить, стоят ли затраты и выгоды от ее интеграции. Альтернатива состоит в том, чтобы написать свои собственные фабричные классы, методы и замыкания: в этой статье объясняется один из шаблонов для создания динамических объектов фабрики и, как таковая, снижается стоимость второго варианта. То, что вы знаете, как делать, обходится дешевле, чем то, что вам еще предстоит изучить, учитывая риск и время внедрения.

Зачем писать свои собственные фабрики?

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

Однако, строго говоря, мы можем перечислить некоторые преимущества подхода «Принеси свой код»:

  • меньше внешних зависимостей : ваш код может быть повторно использован в других проектах без жесткой зависимости от библиотеки, такой как контейнер внедрения зависимостей.
  • Гибкость : каждая инфраструктура или библиотека поддерживает ряд вариантов использования, что усложняет разработку изменений на других осях. Это верно и для вашего кода — никто не может быть закрыт против какой-либо модификации — но, по крайней мере, в вашем коде вы решаете, какие изменения легко поддерживаются, а не автором контейнера.
  • Конфигурирование всегда выполняется с помощью кода, а не экстернализуется в XML или странных массивах. Уровень косвенности тоже имеет свои издержки и является способом введения синтаксического анализа или логических ошибок, которые труднее найти в отношении ошибки в PHP или Java-коде.

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

Сегодня: Карта Сотрудников

Этот шаблон поддерживает внедрение списка соавторов внутри любого объекта. Граф объекта является двудольным и состоит из двух уровней:

  • Первый уровень — это классы для создания экземпляров.
  • Второй уровень — список соавторов.

Вы напрямую запрашиваете только объекты первого уровня к Фабрике, реализующей Карту Сотрудников; второй уровень используется только внутри них для разделения общих обязанностей. Например, первый уровень может охватывать контроллеры HTTP, а второй — репозитории, которые обращаются к базе данных. На втором уровне объекты могут быть головами произвольно сложных деревьев.

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

class ForumController
{
    public function __construct(PostRepository $repository) { /* wiring... */ }
}

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

array(
    'PostRepository' => new DoctrinePostRepository($entityManager)
)

Идентификация запрошенного интерфейса выполняется с подсказкой типа в моем примере, но она может основываться на аннотации или на имени переменной … Все, что вы считаете полезным (но надежным). Подсказка типа заставляет вас использовать интерфейс или абстрактное имя класса и вводит автоматическую проверку проводки; другие ключи более гибкие, но менее подвержены обнаружению ошибок проводки сразу же, во время создания.

Как это работает

class CollaboratorsMapFactory
    public function __construct(array $collaborators)
    {
        $this->collaborators = $collaborators;
    }

    /**
     * @param  string $adapterClassName
     * @return object
     */
    public function create($adapterClassName)
    {
        $reflectionClass = new \ReflectionClass($adapterClassName);
        $constructor = $reflectionClass->getConstructor();
        if ($constructor !== NULL) {
            $arguments = $this->argumentsFor($constructor);
            return $reflectionClass->newInstanceArgs($arguments);
        } else {
            return $reflectionClass->newInstance();
        }
    }

    private function argumentsFor(\ReflectionFunctionAbstract $method)
    {
        $collaborators = array();
        foreach ($method->getParameters() as $parameter) {
            $requiredInterface = $parameter->getClass()->getName();
            $collaborators[] = $this->collaborator($requiredInterface);
        }

        return $collaborators;
    }

    private function collaborator($role)
    {
        if (!isset($this->collaborators[$role])) {
            throw new \RuntimeException("The role $role is missing.");
        }
        $stored = $this->collaborators[$role];
        if ($stored instanceof $role) {
            return $stored;
        }
        throw new \RuntimeException("The role $role is misconfigured.");
    }
} 

Простые расширения

Некоторые модификации легко вводить: эта форма построена, чтобы позволить им. Например:

  • Добавление соавторов.
  • Добавление новых объектов, использующих тот же пул соавторов.
  • Добавление косвенности при создании соавторов, например, отложенная загрузка: вместо объекта вы можете разрешить имена классов и замыкания, изменив методlaborator ().
  • Добавление пользовательских коллабораторов: внутри замыканий выше. Сотрудник может быть краевым узлом большого графа объектов.
  • Добавление разных конфигураций для одного и того же класса второго уровня: снова внутри замыканий выше. Карты не должны иметь уникальных значений.

Жесткие расширения

Принцип Открытого / Закрытого может соблюдаться только для некоторых осей изменения, но не для всех. Если появятся эти вариации, реализация этого шаблона пострадает.

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

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

Выводы

Шаблон — это форма для вашего кода, форма, которую вы можете или не можете принять в зависимости от контекста, предлагаемого вашим приложением. Знайте, что может легко сделать Карта Сотрудников и что требует других шаблонов творчества; используйте эту основанную на отражении реализацию в качестве шаблона, если вы работаете с PHP.