Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Dependency Injection Container
Forum PHP.pl > Forum > PHP > Object-oriented programming
Luneth
Ostatnio zainteresowałem się tym artykułem: http://phpgeek.pl/112/dependency-injection-container/ Ciekawi mnie jedna kwestia, czy instancję Containera przekazywać gdzieś dalej do obiektów i do obiektów w obiektach przez parametr, czy skorzystać z tego, że tablica jest zmienną statyczną i w kolejnych "zagnieżdżeniach" obiektów tworzyć nowy obiekt Containera? Bo jeśli przez parametr to można wywalić zmienną statyczną (większa śladowa wydajność, większy bałagan w parametrach) a jeśli tworzyć nowy obiekt to niby nam ta wydajność spada, bo i zmienna statyczna i do tego kolejne obiekty... np 3 obiekty przy jednym żądaniu. Chociaż nie wiem jak się ma to do zmiennych statycznych, to podobno stosowanie metod statycznych (w większej dawce) odbywa się kosztem spadku wydajności. Co Wy o tym wszystkim myślicie?
Crozin
Wydajność nie ma tu większego znaczenia - chodzi bardziej o wygodę działania.
Tutaj masz przykładową implementację http://components.symfony-project.org/dependency-injection/
Luneth
W tym poście chodzi mi o to, aby głównie się skupić na tej prostej implementacji zaprezentowanej przez autora smile.gif
smentek
A My o tym wszystkim myślimy, że z całą pewnością NIE powinineś przekazywać kontenera jako parametru w metodach (czy konstruktorach) obiektów, które ten że kontener inicjalizuje i w którym one "pływają".

To było było by doprawdy kuriozum. Ideą IoC jest eliminacja sprzężeń w kodzie, czyli ZBĘDNYCH zależności pomiędzy poszczególnymi fragmentami systemu. Tymczasem w ten sposób wprowadził byś dodatkowe zupełnie zbędne sprzężenia.

Po prostu korzystaj z kontenera w taki sposób w jaki opisuje to jego dokumentacja. Przykładowo jeżeli jest:

  1. $container = someIoCContainer::initialise();


To wywołujesz kontener właśnie w taki sposób wszędzie tam gdzie jest potrzebny. Będzie on inicjalizowany tylko za pierwszym razem reszta to odwołania do tego samego obiektu. Poczytaj o singletonach.
Crozin
@smentek: To Tobie radziłbym poczytać o tym wzorcu, bo to co proponujesz:
  1. class AbcMyClass {
  2. public function __construct() {
  3. $this->container = someIoCContainer::initialise();
  4. }
  5. }
To właśnie powód dla którego ten normalny wzorzec ma opinię anty-wzorca... nie takie jest jego zastosowanie! Co więcej takie partactwo przynosi jedynie więcej problemów niż pożytku. Jedyne co zrobiłeś to zamieniłeś global na :: i narzuciłeś sobie dodatkowych sztucznych ograniczeń i, o ironio, zbędnych zależności.

A żeby nie było: celem tego wzorca jest zapewnienie dokładnie jednej instancji danej klasy, a nie globalny dostęp do obiektu - od tego jest global.
smentek
Cytat(Crozin @ 8.09.2010, 22:28:18 ) *
@smentek: To Tobie radziłbym poczytać o tym wzorcu, bo to co proponujesz:
  1. class AbcMyClass {
  2. public function __construct() {
  3. $this->container = someIoCContainer::initialise();
  4. }
  5. }
To właśnie powód dla którego ten normalny wzorzec ma opinię anty-wzorca... nie takie jest jego zastosowanie! Co więcej takie partactwo przynosi jedynie więcej problemów niż pożytku. Jedyne co zrobiłeś to zamieniłeś global na :: i narzuciłeś sobie dodatkowych sztucznych ograniczeń i, o ironio, zbędnych zależności.

A żeby nie było: celem tego wzorca jest zapewnienie dokładnie jednej instancji danej klasy, a nie globalny dostęp do obiektu - od tego jest global.


Rozumiem, i masz rację, że bez singletonu można się tu obejść.

Nie zaproponowałem jednak niczego poza tym żeby nie przekazywać Kontenera po kodzie tworzonej aplikacji z obiektu do obiektu w wywołaniach metod i aby robić to zgodnie z dokumenacją danego kontenera. I jeżeli ten kontener opierał by się na singletonie (przyjmujemy że dobry kontener nie powinien sie na nim opierac) to niestety ale właśnie tak należało by to robić jak to przedstawiłem. A jeżeli na nowo tworzonym obiekcie z urzyciem new to analogicznie należało by tworzyć nowy obiekt tam gdzie byl by potrzebny kontener. I to proszę traktować jako myśl przewodnią mojego postu smile.gif

A już na na pewno nie proponowałem aby kontener uczynić zmienną obiektu, rozumiem że jest to Twoja propozycja? winksmiley.jpg
Crozin
Cytat
Nie zaproponowałem jednak niczego poza tym żeby nie przekazywać Kontenera po kodzie tworzonej aplikacji z obiektu do obiektu
Jeżeli nie przekazywać to... zrobić z niego globala. Jeżeli zrobić z niego "singletonowego globala" to tak jakby użyć zwykłego globala (tylko bardziej "pro"). Idąc dalej nasz kod zyskuje wszystkie "zalety" globala, tj.: zależności od jakiś zewnętrznych, niekontrolowanych źródeł. W dodatku zamiast opierać się na interfejsach (co w teorii powinno być dla OOP w PHP naturalne - no niestety nie wyszło) musimy opierać się na klasach, czyli innymi słowy wymuszamy implementację zależności. A wszystko to by pozbyć się przekazania tej jednej zależności w konstruktorze obiektu. IMO do d..y takie "udogodnienie".
smentek
Ale dlaczego uważasz że kontener miał by mieć zasięg globalny?
Crozin
Bo skoro nie lokalny (i bezpośrednio przekazywany dalej) to musi być globalny? Pokaż może fragment kodu ilustrujący co Ty masz na myśli.
smentek
Ok przyznaję, że daleko mi do eksperta jeżeli chodzi o IoC ale zaryzykuję stwierdzenie że nie musi mieć on zasięgu globalnego (choć może istnieją kontenery które muszą, nie wykluczam tego ). A wiem to z doświadczenia. Przykład: pico

Pierwsze wywołanie linia 31kolejne linia 65. W innych klasach w kodzie, chyba też go użyłem, jak widzisz nie ma on zasięgu globalnego a kod śmiga jak powinien...
dariuszp
Wzorzec jak wzorzec. Jak dla mnie to po prostu ciekawe zastosowanie kolekcji. Sam użyłem coś podobnego w moim systemie.
Główny obiekt przechowuje obiekty odpowiedzialne za użytkownika, sesje, dostęp, przepływ danych itp itd etc.
Do tego można w nim zarejestrować rozszerzenia wszelakiej maści lub "wyrejestrować" usługę sesji zastępując ją własnym mechanizmem.

Fakt że są pewne różnice. Główny obiekt ma zasięg globalny z prostego względu. Daje możliwość modułom (w tym systemie posunąłem się do granic absurdu gdzie nie pytamy już co JEST modułem a co NIE JEST modułem ze względu na to że gdybym tego nie zrobił - szereg modyfikacji które nie raz życzą sobie klienci nastręczały by trudności - a tak jeżeli klient życzy sobie coś specjalnego to nie grzebiąc w kodzie dodaje nowy moduł i ustawiam go w systemie - wyniki po profilowaniu mam zaskakująco dobre więc nie ma co narzekać) dobrać się do interesujących go danych (dane użytkownika, przywileje, dostępne usługi, parametry itp) z każdego miejsca.

Działanie rozwiązania które podałeś jest bardzo zbliżone. Różnica polega na tym że część obiektów jest zaszyta na stałe i mamy tylko i wyłącznie możliwość ich wymiany (tzw rdzeń) na inne a reszta jest rejestrowana i może być wywoływana.
smentek
Cytat
Jak dla mnie to po prostu ciekawe zastosowanie kolekcji


Można na to patrzyć zróżnych stron ale należy pamiętać, że w kolekcjach chodzi o PRZECHOWYWANIE obiektów w jakiś tam inteligentny sposób. Np. kolekcja która nie pozwoli na 2 identyczne elementy, albo kolekcja która nie wpuści elementu o typach innych niż te dozwolone.

Kontenery IoC nie mają z takim zachowaniem nic wspólnego.

W kontenerach chodzi o to aby wprowadzić do systemu kolejną wyraźnie odseparowaną warstwę abstrakcji do której przenosimy inicjalizację obiektów.

@Crozin
Wracając do tego co napisałeś wcześniej:

Cytat
A żeby nie było: celem tego wzorca jest zapewnienie dokładnie jednej instancji danej klasy, a nie globalny dostęp do obiektu - od tego jest global.


Zgadzam się z pierwszą częścią i może Cię źle rozumiem jeżeli tak to mnie popraw, ale mam wrażenie, że uważasz, że wprowadzanie globalności przy pomocy singletonu jest złe a korzystanie z niej przy pomocy global jest ok. A przecież nie chodzi o to że wprowadzanie globalności przez singleton jest złe, chodzi o to że sama globalność jest zła niezależnie od tego czy wprowadzana poprzez singleton czy przez global.

Wracając do globalności kontenera.

Wyobraźmy sobie że mamy globalny kontener mający w sobie referencje do wszystkich już zainicjalizowanych w systemie obiektow. Teraz chcemy zainicjalizować z niego nowy obiekt, który miał by być utworzony z referencjami do obieków które kontener już posiada. Ale nie jakichś tam obiektów tylko konkrtnych obiektów tak jak to zwykle bywa w programach smile.gif. I co się okazuje, i tak musimy powiedzieć kontenerowi wyraźnie których konkretnie z obiektów ma użyć do inicjalizacji nowego obiektu.

A w jaki sposób możemy mu to powiedzieć?

Dostarczając referencję do tych obiektów. Mam rację? smile.gif

A skoro musimy dostarczać tych referencji w momencie inicjalizacji nowego obiektu, to równie dobrze możemy tworzyć sobie nową instancje kontenera zamiast korzystać z jednej globalnej. Globalność kontenra może się przydać w pewnych sytuacjach ale w innych jak wyżej opisywana nie jest przydatna.

Crozin
Tak, źle zrozumiałeś. Napisałem, że robienie z Singletona globala, jest złe, że od tego jest global - ale nie napisałem, że global jest dobry. Bo jest zły (w aplikacjach webowych średnio widzę dla niego zastosowanie).

Kontener może przecież przekazać samego siebie usłudze (tak by był dostępny w jej wnętrzu), a ona już sobie sama wybierze potrzebne jej usługi.

Cytat
to równie dobrze możemy tworzyć sobie nową instancje kontenera zamiast korzystać z jednej globalnej
Przecież jeżeli utworzysz nową instancję to skąd wytrzaśniesz w niej referencje do różnego rodzaju usług?
Luneth
Ja swój problem rozwiązałem tak: Container przechowuje po jednej instancji wybranych klas, nie przechowuje instancji WSZYSTKIEGO (modeli, kontrolerów, widoku) ale np. coś, co tworzone powinno być jedynie raz na jedno żądanie - np. sesje. Instancję containera przekazuję przez paramter do konstruktora - tak, bo jest mi tak wygodnie - do czasu. Kiedy zagnieżdżenia robią się naprawdę zagnieżdżone, gdzieś tam naprawdę nisko, gdzie dostępu do niego nie ma, a przekazywane jest już trochę więcej parametrów - tworzę po prostu nowy obiekt containera. W statycznej zmiennej trzymam tablicę z instancjami klas. Myślę, że to najbardziej kompromisowe rozwiązanie, bo global odpada i nie ma co o to pytać nawet, przekazywania miliona parametrów też odpada.
wookieb
Cytat(Crozin @ 10.09.2010, 07:33:03 ) *
Przecież jeżeli utworzysz nową instancję to skąd wytrzaśniesz w niej referencje do różnego rodzaju usług?

Może zagłębmy się w definicję słów Inversion of Controler = Odwrócenie sterowania.
Nie polega to jedyna na ukryciu zależności ale też m.in odciążenie innych klas od tworzenia i dorzucania do kontenera pojedynczych obiektów.
Dodatkowo, żeby aplikacja była "hermetyczna" obiekt kontenera NIE MOŻE być globalny, statyczny ale LOKALNY. Czyli, przekazywać go tam gdzie potrzeba.
Co nam to daje? Nie tworzymy, np połączenia z bazą danych kiedy jest ono kompletnie niepotrzebne, masy obiektów których nie użyjemy.

Samo wykorzystanie DIC jest bardzo elastyczne i wygodne aczkolwiek wydajność... to już inna kwestia. Parsowanie xml-a, badania zależności, badanie koniecznych parametrów do przekazania trochę czasu zajmuje. U siebie zrezygnowałem z xml-a i zależności wstrzykuje "ręcznie" w kontenerze.

Cytat(Luneth @ 12.09.2010, 11:23:40 ) *
W statycznej zmiennej trzymam tablicę z instancjami klas. Myślę, że to najbardziej kompromisowe rozwiązanie, bo global odpada i nie ma co o to pytać nawet, przekazywania miliona parametrów też odpada.


Czyli po prostu inaczej ubrałeś słówko "global". Każdy obiekt statyczny, singleton jest po prostu bezpieczniejsza forma globala także nie tędy droga. Oczywiście wszystko zależy od gustu i upodobań.
Luneth
Okej, to mogę wywalić zmienną statyczną i wszędzie instancję kontenera przekazywać przez parametr, nawet tam gdzie to więcej komplikuje niż pomaga - tędy droga? Zresztą jaki był cel wprowadzania możliwości deklarowania zmiennych statycznych? Myślałem, że po to, aby korzystać z nich w takich przypadkach, niewielkiej ilości na skalę projektu (ja np użyłem dwóch zmiennych statycznych w całym swoim projekcie). Bo jeśli tego się nie stosuje, bo jest be i kojarzy się z globalem to po co pisać o tym w manualu w ogóle..
wookieb
Właściwości statyczne przydają się w miejscach gdzie klasa nie musi tworzyć instancji i jej wewnętrzne właściwości nie wpływają na resztę aplikacji. Przykładem jest mój kalkulator ze stopki.

Cytat
Okej, to mogę wywalić zmienną statyczną i wszędzie instancję kontenera przekazywać przez parametr, nawet tam gdzie to więcej komplikuje niż pomaga - tędy droga?

Napisałem, że to zależy od upodobań oraz gustu. Jeżeli przekazujesz instancję kontrolera tam gdzie jest to wskazane powodujesz tym samym, że twoja aplikacja jest hermetyczna. Bardzo łatwo ją testować a także uruchamiać równolegle z innym egzemplarzami twojej aplikacji (2 lub więcej aplikacji odpalonych w jednym skrypcie).


Cytat
Bo jeśli tego się nie stosuje, bo jest be i kojarzy się z globalem to po co pisać o tym w manualu w ogóle..

Jest dobre przekonanie, że globala nie powinno się stosować i jestem jak najbardziej za. Ale jeżeli ktoś się zapyta "co poza global?" a ty odpowiesz mu, że klasa statyczna to wcale nie pomożesz a jedynie usadowisz użytkownika w tym samym punkcie. Dlatego o tym wspomniałem. PHP powstał i jest dla początkujących użytkowników, dlatego GLOBAL pozostał jako taki. Dla bardziej zaawansowanych programistów jest on bezużyteczny.
Luneth
Nie zrobiłem klasy statycznej, jedynie atrybut przechowujący instancje jest statyczny, a każdą instancję pobiera się odp. getterem(który statyczny już nie jest), który umożliwia też umieszczenie różnych parametrów, ale być może okaże się, że jest to jakieś pomieszanie, a właściwym byłoby przekazywanie wszędzie przez parametr, w wyniku czego container będę przekazywany wszędzie - do modelu, do widoku, do kontrolera. Jeśli takie rozwiązanie byłoby uznane przez profesjonalnych programistów to ja jestem gotów zrezygnować ze zmiennej statycznej w swojej sytuacji na rzecz przekazywania parametrem wszędzie i zawsze.
Crozin
Cytat
Nie zrobiłem klasy statycznej
Tak, wiemy - bo PHP czegoś takiego nie obsługuje.
Cytat
jedynie atrybut przechowujący instancje jest statyczny
Innymi słowy masz coś takiego?
  1. <?php
  2.  
  3. class Container {
  4. protected static $instances = array();
  5.  
  6. public function get($name) {
  7. return self::$instances[$name];
  8. }
  9. }
  10.  
  11. // ....
  12. $c = new Container();
  13. $c->set('abc', new Abc());
  14.  
  15. // ...
  16. $c = new Container();
  17. $abc = $c->get('abc');
  18.  
  19. // ...
  20. $c = new Container();
  21. $abc = $c->get('abc');
Jeżeli tak to masz z du.y rozwiązanie, bo ograniczasz się do jednego kontenera, którego interfejs jest strasznie nielogiczny. Składowe statyczne służą do przechowywania danych związanych z klasą, nie obiektem - a Ty operujesz każdorazowo na nowym obiekcie.
Luneth
Owszem, mam coś na tej zasadzie. Nie tworzę za każdym razem nowego obiektu, zaznaczam smile.gif Okej, załóżmy że porzuciłem zmienną statyczną, obiekt kontenera wszędzie przekazuję. Gdzie jest ten brak logiki teraz? Potrzebuję jednej zmiennej która będzie w sobie zawierała odniesienia do paru obiektów: sesji, bazy danych etc. Równie dobrze mogę na początku zadeklarować taką tablicę bez wsadzania tego w klasę i to przekazywać. To już jest bardziej logiczne?
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.