Dependency-injection in real life

Buduję naprawdę minimalny framework MVC, aby zwiększyć moją wiedzę PHP i rzucić sobie wyzwanie. Doszedłem do punktu, w którym zajęcia zaczynają być zależne od siebie nawzajem do pracy. Dependency injection wydaje się być rozwiązaniem tego problemu i są używane przez niektóre z wielkich frameworków wokół.

Znalazłem Bucket na Githubie i przez jakiś czas grzebałem w nim, aby zrozumieć podstawy. to, czego nie mogę ogarnąć, jest jednak wtedy, gdy jest to właściwe aby utworzyć kontener?

Zrobienie jednego dużego pojemnika zawierającego wszystkie możliwe klasy, które mogą być potrzebne wydaje mi się nic innego, jak przeciwnego do zamierzonego i nie wyobrażam sobie, że jest to dobra praktyka. Wydaje się, że przepis na złe wyniki przynajmniej.

W alternatywie, która polega na zrobieniu wielu pojemników, nadal nie rozumiem, jak zawsze śmierdzące Singletony nie są już potrzebne.

Powiedzmy, że miałbym następujący kod:

$session_container = new bucket_Container();
$session_container->create('Database');
$session_container->create('Database_Sessions');

$log_container = new bucket_Container();
$log_container->create('Database');
$log_container->create('Database_Log');

Więc tutaj mamy dwa kontenery, lub w tym przypadku wiadra dla dwóch zupełnie różnych zastosowań, które są wzajemnie zależne od klasy Database.

Moja logika mówi mi, że powyższy kod utworzy dwie niezależne instancje Database - class, co oznacza, że nadal będę musiał zrobić Database - class a singleton, aby upewnić się, że współbieżne instancje mojego połączenia z bazą danych nie występują?

Czy to prawda?

Author: Charles, 2011-02-19

3 answers

Nie wiem zbyt wiele o konkretnej lib, ale zakładając, że pozwala na użycie fabryki, niech fabryka zwróci tę samą instancję.

Edit: Ok, to jest po prostu na stronie indeksu Bucket GitHub.

class MyFactory {
  function new_PDO($container) {
    return new PDO("mysql:host=localhost;dbname=addressbook", "root", "secret");
  }
}

$bucket = new bucket_Container(new MyFactory());
$db = $bucket->get('pdo');

Więc w Twoim przypadku możesz po prostu zrobić:

class MyFactory {
   private $pdo;
   function new_Database($container) {
     if($this->pdo){
         return $this->pdo;
     }
     return $this->pdo = new PDO("mysql:host=localhost;dbname=addressbook", "root", "secret");
   }
}
$factory = new MyFactory();

$session_container = new bucket_Container($factory);
$session_container->create('Database_Sessions');

$log_container = new bucket_Container($factory);
$log_container->create('Database_Log');
Coś w tym stylu. To nie wygląda jak nauka rakietowa.

Edit2: nie mam wystarczającej ilości punktów rep, aby skomentować pytanie( trochę głupie), ale w odpowiedzi na twoje" modułowość": pomyśl o kontenerze jako o "klej" aplikacji. Rzeczywiście, jeśli masz dużą aplikację, możesz chcieć "przykleić" tylko do izolowanej części aplikacji. To ważny problem z hermetyzacją. Ale nawet wtedy nadal potrzebujesz pojemnika, który obsługuje Wtrysk na najwyższym poziomie abstrakcji. Jeśli po prostu utworzysz osobny kontener dla każdej części aplikacji, albo skończysz z niepotrzebnym duplikowaniem wystąpień, albo będziesz musiał zastosować inny poziom zarządzania wystąpieniami, który nie poprawia się enkapsulacja w dowolny sposób: nadal udostępniasz instancje między różnymi częściami aplikacji.

Radzę użyć jednego kontenera na poziomie bootstrap. Jeśli chcesz dodać enkapsulację dla określonych części aplikacji( Moduły, wtyczki, cokolwiek), użyj "kontenerów potomnych". Kontener potomny dziedziczy instancje z kontenera rodzica, ale kontener rodzica nie wie nic o dziecku (jeśli o niego chodzi, nadal jest kawalerem;)). Możliwe, że Bucket obsługuje to domyślnie, wiem, że inne kontenery DI tak. Jeśli nie, jest to naprawdę łatwe do wdrożenia za pomocą dekoratora. Wyobraź sobie coś takiego:

class MyContainerType extends bucket_Container {

    private $_parent;
    private $_subject;

    public function  __construct($factory = null, bucket_Container $parent = null){
        $this->_parent = $parent;
        $this->_subject = new bucket_Container($factory);
    }

    public function get($key){
        $value = $this->_subject->get($key);
        if($value){
            return $value;
        }
        return $this->_parent->get($key);
    }
    /**
     * Override and delegation of all other methods
     */
}
 9
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-20 10:45:19

Zrobienie jednego dużego pojemnika z każdą możliwą klasą, która może być potrzebna, wydaje mi się mało produktywne i nie mogę sobie wyobrazić, że jest to dobra praktyka. Wydaje się, że przepis na złe wyniki przynajmniej.

Wręcz przeciwnie. To jest dokładnie to, co byś zrobił z pojemnikiem di. Kontener tworzy instancje obiektów tylko na żądanie, więc praktycznie nie ma narzutu na zarządzanie wszystkimi klasami singleton.

Największy problem z di jest rozróżnianie między obiektami współdzielonymi (rzeczy, które zwykle są uważane za singletony) i obiektami przejściowymi (obiekty, które mają wiele instancji poprzez normalny przepływ aplikacji.). Te pierwsze są łatwo zarządzane przez di. Te ostatnie nie pasują. Posiadanie tych dwóch" rodzajów " obiektów wyraźnie rozróżnianych może wydawać się trochę kłopotliwe, ale jest naprawdę bardzo korzystnym efektem ubocznym korzystania z kontenera di.

 6
Author: troelskn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-20 15:01:02

Jeśli obawiasz się wielu jednoczesnych połączeń, możesz po prostu użyć mysql_pconnect () lub equivelant dla używanej bazy danych. Sprawdzi, czy połączenie jest już otwarte i użyje istniejącego połączenia, jeśli jest.

Jeśli chodzi o kontener, widziałem to na dwa sposoby, które zdaje się być świadome obu. Pierwsza metoda polega na tym, aby Framework odczytał schemat bazy danych i stworzył klasy dla każdej tabeli. Osobiście nie podoba mi się takie podejście. Symfony jest jednym z ram, które to robi (za pomocą doktryny ORM).

Bardziej preferowaną metodą, jaką widziałem, jest posiadanie ogólnego kontenera, który zasadniczo buduje sql dla Ciebie, biorąc pod uwagę tabelę, kolumny i akcję. Jest to podejście przyjęte przez codeIgniter:

$query = $this->db->get('mytable');
$query = $this->db->get_where('mytable', array('id' => $id), $limit, $offset);
 0
Author: regality,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-19 17:41:27