Внедрение зависимостей с фабриками

Я практиковал внедрение зависимостей конструктора во всем своем PHP-приложении. Я не хотел засорять свой код созданием объектов, поэтому фабрики пришли на помощь, или, по крайней мере, я так думал.

Я начал связывать компоненты с фабриками, затем некоторые фабрики начали использовать другие фабрики для получения зависимостей, отлично, весь код создания хранится в одном месте. Однако, как только фабрики начинают использовать друг друга (или, как в приведенном ниже коде, сами по себе), я столкнулся с проблемами циклической зависимости, которые просто не могут быть решены. Например, моя MapperFactory использует себя для внедрения картографов с другими картографами (они нужны друг другу для построения полного графа объектов «нетерпеливой загрузки»):

class MapperFactory
{   
    public function create($type)
    {
        switch (true) {
            case 'Item':
                $mapper = new ItemMapper(
                    $this->create('Field')  
                );               
                break;
            case 'Field':
                $mapper = new ItemMapper(
                    $this->create('Item')  
                );
                break;
            default:
                throw new Exception('Unknown mapper');
        }
        return $mapper;
    }

}

$mf = new MapperFactory();
$mf->create('Item');

Это упрощенный пример, но проблема становится все более распространенной по мере разработки приложения. Ошибка возврата из PHP (установлен xdebug):

Fatal error: Maximum function nesting level of '100' reached, aborting!

Полностью понимаю, почему PHP жалуется (хотя не ожидал, что это произойдет, TBH).

Мой вопрос в том, я полностью упустил смысл фабрик? правильно ли я использую фабрики? Казалось бы, нет, но кроме циклической зависимости (довольно серьезной, но), фабрики — это элегантное решение для сокрытия всей логики построения/проводки от основного приложения.


person user1595982    schedule 13.08.2012    source источник
comment
Я не думаю, что это проблема с заводами. Во-первых, я не понимаю часть переключателя (правда). Разве это не должен быть переключатель ($ type)? Во-вторых, ваш код (если я правильно понял) бесконечно повторяется. Возможно, вам следует создавать подполе или подэлемент только тогда, когда вы будете готовы использовать эту переменную экземпляра. Или придумать что-то, что завершит рекурсию.   -  person Marvo    schedule 13.08.2012
comment
Я думаю, switch (true) ... это не то, что вы хотели написать. Но даже с switch ($type) вы получите бесконечный цикл, если $type равно Field или Item.   -  person Yoshi    schedule 13.08.2012


Ответы (2)


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

$itemMapper = new Mapper();
$fieldMapper = new Mapper();
$itemMapper->setRelatedMapper($fieldMapper);
$fieldMapper->setRelatedMapper($itemMapper);

А затем используйте переключатель только для возврата маппера. Это должно избавиться от циклических зависимостей при создании объектов.

Сказав это, если вы делаете это как нечто вроде ИЛИ/М для подключения к базе данных, вам, возможно, следует изучить такие вещи, как Doctrine2 или Propel, просто чтобы избавить себя от необходимости изобретать колесо, когда уже есть опробованные и есть проверенные решения.

person Ivan Pintar    schedule 13.08.2012
comment
Да, я экспериментировал с сеттерами, хотя считаю зависимости «требованием», поэтому они требуются в конструкторе. Но определенно что-то, на что мне придется посмотреть снова. Что касается ORM, мне также нужно сохранять объекты через службы SOAP и REST, поэтому мне нужно поддерживать гибкость. - person user1595982; 14.08.2012
comment
Да, конструктор — это способ обеспечения отношений, но в вашем примере он создает цикл, потому что элемент создает поле, которое создает элемент и т. д. Используя сеттеры, вы просто делитесь ссылками между объектами, а пока дамп var или что-то подобное также покажет бесконечную иерархию объектов, по крайней мере, она не ломается. И вы создаете только два экземпляра, а не бесконечность... - person Ivan Pintar; 14.08.2012

Похоже, что метод create MapperFactory вызывает бесконечный цикл.

switch(true) {
  case 'Item' : // this will always be selected http://php.net/manual/en/language.types.type-juggling.php
    $mapper = new ItemMapper(
      $this->create('Field'); // Forces loop,
    );

Если коммутатор ищет совпадение TRUE, тогда операция case должна быть логической.

switch(true) {
  case $type == 'Item' :
    // ...
    break;
  case $type == 'Field' :
    // ...
 }
person emcconville    schedule 13.08.2012
comment
Да, извините, учитывая контекст вопроса (круговые зависимости), это непростительно. Это должен был быть переключатель ($ type). - person user1595982; 13.08.2012