Kom ihåg mig?
Home Menu

Menu


Php dependency injection

Ämnesverktyg Visningsalternativ
Oläst 2013-03-11, 14:49 #1
danjel danjel är inte uppkopplad
Medlem
 
Reg.datum: Nov 2003
Inlägg: 214
danjel danjel är inte uppkopplad
Medlem
 
Reg.datum: Nov 2003
Inlägg: 214
Earth Php dependency injection

Hur brukar ni hantera dependencies i php oo programmering ?

Om man läser runt så verkar DI vara att föredra framför exempelvis Singelton eller Service locator patterns.
Men de exempel jag hittar visar på ganska enkla exempel,
är tanken att -alla- dependencies bör injectas ? Ibland kan ju ett enda request kanske använda 50 klasser (eller mer).
danjel är inte uppkopplad   Svara med citatSvara med citat
Oläst 2013-03-11, 16:31 #2
Clarence Clarence är inte uppkopplad
Administratör
 
Reg.datum: Jan 2003
Inlägg: 1 974
Clarence Clarence är inte uppkopplad
Administratör
 
Reg.datum: Jan 2003
Inlägg: 1 974
Om en klass är beroende av 50 andra klasser så är det dags att refaktorisera den. Men att ett request i slutändan använt 50 klasser är inte så relevant.

Om du tänker dig en Main/applikations-klass och att du sedan går till Controller och sedan en funktionsklass och sedan något annat osv osv... Då kan det var intressant att använda en service-locator med enkel konfiguration istället för att skicka med ett gäng beroende ner i en lång kedja. Målet är inte att hålla sig borta i från service locators utan att skapa så mycket som möjligt i komponenter som inte är beroende av service locatorn. Det som är strikt bundet till just din nuvarande applikation kan vara beroende av en implementation i din applikation..

Om du har en mail-klass så är den kanske beroende av t ex en transport-klass för hur mailen ska skickas och en loggningsklass (som i sin tur är beroende av en handlerklass för hur det ska loggas). Att injicera dessa direkt i mail-klassen gör både att den blir direkt återanvändbar och att det är väldigt enkelt att ändra dessa parametrar i olika driftsmiljöer eller sajter.

Men varifrån dessa hämtas i din applikation spelar mindre roll. Du kan t ex ha en service locator med en konfigurations-service. I din controller eller i en extension av din controller /main-klass kan du hämta den och initialisera Mailer-klassen innan du ska skicka ett mail (och se till att använda delade instanser om du ska göra det fler gånger), det gör i princip att delar av din applikation blir mindre återanvändbar, men det är just sådan kod som är specifik för just din implementation.

Det viktiga är att du försöker identifiera och tänka över de återanvändbara delarna och strukturera dessa med direkt DI. Om en klass har ett specifikt ansvarsområde (SoC - Separation of concerns) blir det ofta lätt att se vilka klasser som går att återanvända. I slutändan bör det mest vara sammanbindande klasser och väldigt specifik funktion som ligger utanför.
Clarence är inte uppkopplad   Svara med citatSvara med citat
Oläst 2013-03-11, 17:24 #3
danjel danjel är inte uppkopplad
Medlem
 
Reg.datum: Nov 2003
Inlägg: 214
danjel danjel är inte uppkopplad
Medlem
 
Reg.datum: Nov 2003
Inlägg: 214
OK, tack för ditt svar.
Ja menade alltså att en http request och inte en enkild klass i slutändan använt 50 klasser.

Ja det låter nog vettigt att inte helt utesluta Service locators som du säger. Har sett exempel där man initierar alla object / factories som en "request" behöver och som sedan injectas i själva MVC Controllern som ska hantera requesten. Typ:
<?php
$f = new MyControllerFactory(..);
$c= new MyController();
$c->index($f);
?>

Men det känns bökigt

Om man tänker på ett exempel som ovan men att man inte skickar in dependencies direkt,
bör själva "index" metoden anropa en ServiceLocator ? , om vi nu säger att den ska anropa
en Model klass som i sin tur ska skicka ett mail och spara i en db

<?php
class MyController{

public function index()
{
$m= new MyModel( new ServiceLocator() );
$m->sendMail();

}

}
?>
danjel är inte uppkopplad   Svara med citatSvara med citat
Oläst 2013-03-11, 18:42 #4
Clarence Clarence är inte uppkopplad
Administratör
 
Reg.datum: Jan 2003
Inlägg: 1 974
Clarence Clarence är inte uppkopplad
Administratör
 
Reg.datum: Jan 2003
Inlägg: 1 974
Vad är det för modell som bör vara ansvarig för att skicka mail är min första fundering. Lite fler funderingar med lite kod;

Kod:
/* skapa objekt av ditt request för att göra request-hanteringen testbar, går att "sno" direkt av t ex Symfony2 eller ZF2 */
public function index(Request $request) 
{ 
    $model = new Model($request->get('this'), $request->get('that')); // this & that är query strings modellen hanterar

    $model = $this->createModel('Model'); /* Om dina models t ex behöver databas 
blir det lite fulare. I detta exempel skulle en DI container som satts från 
ControllerFactory kunna injectas via setter till modellen om den har en viss property
 eller har en setter för DBn. Använd hellre en repository klass framför som spottar ut modellen. */

/* 'mailer' bör läggas till i service locatorn av din controller factory i ditt 
exempel. Men den bör EJ initieras då den säkerligen inte används till alla requests, 
det görs lättast med closures. Pimple för en väldigt lättviktig DI container med
 closures och shared instances. */
    $this->get('mailer')->send($model->getTitle(), $model->getText()); 
}

Senast redigerad av Clarence den 2013-03-11 klockan 18:42 Anledning: Dum indentering av kommentarer.
Clarence är inte uppkopplad   Svara med citatSvara med citat
Oläst 2013-03-12, 10:53 #5
danjel danjel är inte uppkopplad
Medlem
 
Reg.datum: Nov 2003
Inlägg: 214
danjel danjel är inte uppkopplad
Medlem
 
Reg.datum: Nov 2003
Inlägg: 214
Citat:
Ursprungligen postat av Clarence Visa inlägg
Vad är det för modell som bör vara ansvarig för att skicka mail är min första fundering.
Ja det var ett dumt exempel, vad jag var ute efter var väl principen att en "model" kan göra flera saker tex maila och spara i db m.m.
När jag menar model menar jag mer ett "lager" , låt säga ett "Service lager" och inte en egentlig model klass.

Citat:
Ursprungligen postat av Clarence Visa inlägg
Kod:
/* skapa objekt av ditt request för att göra request-hanteringen testbar, går att "sno" direkt av t ex Symfony2 eller ZF2 */
public function index(Request $request) 
{ 
    $model = new Model($request->get('this'), $request->get('that')); // this & that är query strings modellen hanterar

    $model = $this->createModel('Model'); /* Om dina models t ex behöver databas 
blir det lite fulare. I detta exempel skulle en DI container som satts från 
ControllerFactory kunna injectas via setter till modellen om den har en viss property
 eller har en setter för DBn. Använd hellre en repository klass framför som spottar ut modellen. */

/* 'mailer' bör läggas till i service locatorn av din controller factory i ditt 
exempel. Men den bör EJ initieras då den säkerligen inte används till alla requests, 
det görs lättast med closures. Pimple för en väldigt lättviktig DI container med
 closures och shared instances. */
    $this->get('mailer')->send($model->getTitle(), $model->getText()); 
}
Ok , om jag spinner vidare på ditt exempel,
Anta att man vill skapa en användare i db och samtidigt skicka ett mail.
(Nu blir ju model ett litet missvisande namn kanske..)

Kod:
//antar detta är en metod som inte ligger i controllern men på ett lämpligt ställe, en separat ControllerFactory klass(?)
public function createModel($name) 
{ 

if($name=='UserService')
{
 return new UserService( new UserRepository( new DB() ) , Mailer::getInstance() );
//nu är inte pimple använt 
}

}

//och sedan index metoden

public function index(Request $request) 
{ 
    $model = $this->createModel('UserService');

   $user = new User($request->get('username'), $request->get('userpassword'));

   $model->registerUser( $user ); //denna använder repository och mail klassen
)
Är detta korrekt rent principellt? hmm nu är väl kanske inte Service locator använt ..
danjel är inte uppkopplad   Svara med citatSvara med citat
Oläst 2013-03-12, 13:22 #6
Clarence Clarence är inte uppkopplad
Administratör
 
Reg.datum: Jan 2003
Inlägg: 1 974
Clarence Clarence är inte uppkopplad
Administratör
 
Reg.datum: Jan 2003
Inlägg: 1 974
Citat:
Ursprungligen postat av danjel Visa inlägg
Ja det var ett dumt exempel, vad jag var ute efter var väl principen att en "model" kan göra flera saker tex maila och spara i db m.m.
När jag menar model menar jag mer ett "lager" , låt säga ett "Service lager" och inte en egentlig model klass.



Ok , om jag spinner vidare på ditt exempel,
Anta att man vill skapa en användare i db och samtidigt skicka ett mail.
(Nu blir ju model ett litet missvisande namn kanske..)

Kod:
//antar detta är en metod som inte ligger i controllern men på ett lämpligt ställe, en separat ControllerFactory klass(?)
public function createModel($name) 
{ 

if($name=='UserService')
{
 return new UserService( new UserRepository( new DB() ) , Mailer::getInstance() );
//nu är inte pimple använt 
}

}

//och sedan index metoden

public function index(Request $request) 
{ 
    $model = $this->createModel('UserService');

   $user = new User($request->get('username'), $request->get('userpassword'));

   $model->registerUser( $user ); //denna använder repository och mail klassen
)
Är detta korrekt rent principellt? hmm nu är väl kanske inte Service locator använt ..
Jag har ett par principiella invändningar:

Att ha en if-clause på klassnamnet i controllern blir lätt väldigt bloated, if-listor/switch statements växer ALLTID. En idé är att du dels använder en container för dina dependencies som du skickar in till controllern. Sedan har du i userService t ex en publik lista på vad du vill skicka in t ex array('db', 'mailer') som är keys i din DI container. Det finns givetvis snyggare sätt, men det är ett enkelt sätt att komma runt väldigt mycket if/switch-satser. Se t ex Symfony 2 för ett otroligt smidigt och flexibelt system för det. Jag är dock tveksam till hur decoupled just den komponenten är.

Det andra problemet är ditt sätt att använda Mailer (singleton) och db (intern konfiguration). Felet med både förklaras bättre i ett tal av ZFs lead developer; https://speakerdeck.com/weierophinne...tiful-software Tror du har video på hans sajt också, men den verkar nere när jag skriver det här.
Clarence är inte uppkopplad   Svara med citatSvara med citat
Oläst 2013-03-12, 20:33 #7
coredevs avatar
coredev coredev är inte uppkopplad
Bara ett inlägg till!
 
Reg.datum: Sep 2007
Inlägg: 1 554
coredev coredev är inte uppkopplad
Bara ett inlägg till!
coredevs avatar
 
Reg.datum: Sep 2007
Inlägg: 1 554
Citat:
Ursprungligen postat av danjel Visa inlägg
...
$f = new MyControllerFactory(..);
$c= new MyController();
$c->index($f);
...
Helt OT, men kunde inte låta bli när jag såg "...Factory":
http://harmful.cat-v.org/software/_j...em-factory.jpg
coredev är inte uppkopplad   Svara med citatSvara med citat
Svara


Aktiva användare som för närvarande tittar på det här ämnet: 1 (0 medlemmar och 1 gäster)
 

Regler för att posta
Du får inte posta nya ämnen
Du får inte posta svar
Du får inte posta bifogade filer
Du får inte redigera dina inlägg

BB-kod är
Smilies är
[IMG]-kod är
HTML-kod är av

Forumhopp


Alla tider är GMT +2. Klockan är nu 08:56.

Programvara från: vBulletin® Version 3.8.2
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Svensk översättning av: Anders Pettersson
 
Copyright © 2017