Инжектиране на зависимост с фабрики

Практикувам инжектиране на зависимости на конструктора в моето 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
Не мисля, че проблемът е във фабриките. Първо, не разбирам частта за превключване (true). Това не трябва ли да е switch ($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);

И след това използвайте превключвателя само за да върнете картографа. Това трябва да се отърве от кръговите зависимости при създаване на обекти.

Като казах това, ако правите това като нещо като OR/M, за да се свържете с база данни, може би трябва да потърсите неща като Doctrine2 или Propel, само за да си спестите неприятностите да изобретявате колелото, когато вече има изпробвани и изпитани решения там.

person Ivan Pintar    schedule 13.08.2012
comment
Да, бях експериментирал със сетери, въпреки че считам зависимостите за „изискване“, което ги прави задължителни в конструктора. Но определено нещо, което ще трябва да погледна отново. Що се отнася до ORM, трябва да поддържам обекти и чрез SOAP и REST услуги, така че трябва да поддържам гъвкавостта. - person user1595982; 14.08.2012
comment
Да, конструкторът е начин за налагане на връзките, но във вашия пример той създава цикъл, защото елементът създава поле, което създава елемент и така нататък... С помощта на сетери вие просто споделяте препратките между обектите и докато var dump или нещо подобно също би показало безкрайна йерархия от обекти, поне не се прекъсва. И вие създавате само два екземпляра, за разлика от безкрайност... - 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 съвпадение, тогава операцията за регистър трябва да е булева

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