Dicontainer a controller – PHP – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Dicontainer a controller – PHP – Fórum – Programujte.comDicontainer a controller – PHP – Fórum – Programujte.com

 

panika
~ Anonymní uživatel
6 příspěvků
23. 12. 2015   #1
-
0
-

Ahoj chci se zeptat na dicontainer, na jeho funkci.Dicontainer má za ukol poskládat závislosti a vytvořit objekty contolleru, a tyto objekty udržuje a spravuje (alespoň tak to chápu). Podle této logiky by na každý kliknutí (na každou akci uživatele) musel být jeden Dicontainer, protože každé kliknuti není stejné a potřebuje jiný controller a jiné závislosti ,

Má otázka je tedy : Musím na každý controller  udělat jeden DiContainer?. nebo se to dá nějak zautomatizovat?. 

Nahlásit jako SPAM
IP: 89.103.9.–
ondrej39+1
Věrný člen
23. 12. 2015   #2
-
0
-

#1 panika
DIC potřebuješ jen jeden. Vždycky. Nikdy bys neměl potřebovat cí než jeden. Samotné kontrolery by o DIC nikdy nic neměly vědět, ale jako se vším v programování, existují různé DIC a také různé frameworky, které DIC spravují jiným způsobem... Některé DIC nazbývají jenom jako DI, jinde ho najdeš pod názvem Context nebo Registry.

Název není důležitý, dokud kontrolery o DIC nevědí, jakmile o něm začnou vědět, stává se z DIC service locator a vzniká ti problém, že do kontrolerů vkládáš nejen závislosti, které kontroler skutečně potřebuje, ale předáváš mu celou aplikaci.

K tvému dotazu, jak je to tedy se závislostmi z hlediska DIC? DIC by měl být schopný na základě určité konfigurace poskládat třídy, i když mají zanořené závislosti na několika úrovních. Jedním z takový DIC je například Dice. Pomocí \DireRule jsi schopný nakonfigurovat si (nejčastěji z configu) jaká se má například tahat implementace určitého interface.

Vytváření kontrolerů pak už můžeš řešit například router, který vytvoří třídu pomocí reflexe a requested url.

DIC by jinak měl figurovat jako tzv. slabý singleton. To znamená, že nebude mít privátní konstruktor a veřejnou statickou metodu například getInstance, ale v rámci běhu tvé aplikace si ty sám zajistíš, že se ti vytvoří pouze jedna a jediná instance tohoto kontejneru. Pouze jednou se zavolá new.

Nahlásit jako SPAM
IP: 46.39.172.–
Inject all the dependencies!
panika
~ Anonymní uživatel
6 příspěvků
23. 12. 2015   #3
-
0
-

#2 ondrej39
aha dík, tak tedy ta magie poskladání závislostí je v konfiguraci, mohl by jsi mi ukázat na nějakým teoretickým přikladu jak takový konfigurátor vypadá. z čeho ten konfigurátor vychází, nebo tak něco?.

Nahlásit jako SPAM
IP: 89.103.9.–
ondrej39+1
Věrný člen
23. 12. 2015   #4
-
0
-

#3 panika
Zítra ti něco sesmolím, pokud mě někdo nepředběhne. :-)

Nahlásit jako SPAM
IP: 46.39.172.–
Inject all the dependencies!
panika
~ Anonymní uživatel
6 příspěvků
23. 12. 2015   #5
-
0
-

#4 ondrej39
dik , zatím jsem pročetl  tu stránku na kterou jsi odkazoval a z ní mi vyplývá že co akce uživatele to nějaké pravidlo, takže nebudu  mít hafo DIC ale hafo pravidel buď v podobě pole nebo v xml dokumentu, možná že trochu přeháním, ono to asi nebude hafo pravidel ale trochu mně děsí to parsování xml dokumentu jen proto abych načetl nějaké pravidlo a co si budeme nalhávat kdybych pravidla načítal z pole, tak to taky není zrovna šlágr, teda jestli jsem to pochopil správně a ty pravidla se načítají z konfiguračních souborů a předávají se do konstruktoru DIC a ten podle těch pravidel vytvoří příslušný objekty atd. Radši si počkám na tvůj příklad možná že jenom zbytečně panikařím :-);

Nahlásit jako SPAM
IP: 89.103.9.–
ondrej39+1
Věrný člen
24. 12. 2015   #6
-
0
-

#5 panika
Na první pohled se může zdát, že pokud máš obrovský projekt, čeká tě šílené množství konfigurací (abys nakonfiguroval každou třídu). Realita je ovšem jiná. Nakonfigurovat musíš pouze takové třídy, u nichž není předem známo, co se bude vkládat do hodnot konstruktoru.

Existují ovšem třídy, které konfiguraci vůbec nepotřebují, například nemají v konstruktoru žádné parametry. Faktorky jsou jedním z těchto tříd.

class Car
{
}

class MyCarFactory
{
    public function __construct() { }

    public function createCar()
    {
        return new \Car();
    }
}

Pokud bys chtěl takovou faktorku používat v servisní vrstvě, pak na základě DI musíš faktorku nějakým způsobem předat, nejčastěji v __construct metodě.

class CarService
{
    /** @var \MyCarFactory
    private $carFactory;

    /**
     * @param \MyCarFactory $carFactory
     */
    public function __construct(\MyCarFactory $carFactory)
    {
        $this->carFactory = $carFactory;
    }

    public function foo()
    {
        $car = $this->carFactory->createCar();
    }
}

Ačkoliv máš servisní třídu, která používá faktorku na auta, Dice ti tuto servisní třídu vytvoří úplně jednoduchým zápisem.

$dice->create('\CarService');

Tento postup je možný, protože pro vytvoření CarService potřebuješ pouze třídu MyCarFactory, která ovšem k jejímu vytvoření nepotřebuje žádnou konfiguraci. To Dice přes reflexi rozpozná. Samozřejmě ne vždycky vytváříš třídu, která žádnou konfiguraci nemá.

Zpravidla ti mohou nastat dvě situace:

  1. Vytváříš třídu, která ve svém konstruktoru nemá další třídy, ale přímo konkrétní parametry (stringy, čísla, atp.)
  2. Chceš získat konkrétní implementaci nějakého rozhraní

V obou těchto případech potřebuješ vytvořit nějakou konfiguraci, například pomocí nějakého XML souboru, jak jsi psal. V této konfiguraci pak budeš mít uvedeny vstupní parametry, které se mají do vytvářené třídy vkládat.

Má to několik plus:

  • konfiguraci máš na jedno místě a máš o ní přehled
  • jsi nucen dodržovat DI, následkem čehož jsi schopen psát unit testy oddělené od funkčnosti zbytku aplikace
  • nemusíš psát vlastní faktorky pro vytváření procesů a servisních služeb

Obecně vzato, pokud se rozhodneš použít nějaký DIC jako třeba diskutovaný Dice, pak, jak už jsem psal, by o něm měla být zmínka pouze v bootstrapperu, ale tvá doména by již měla být založena na samotných objektech a službách, které jsi namodeloval ty.

Nahlásit jako SPAM
IP: 46.39.172.–
Inject all the dependencies!
panika
~ Anonymní uživatel
6 příspěvků
24. 12. 2015   #7
-
0
-

#6 ondrej39

Super, dík, to bylo to co jsem potřeboval vědět, že nebudu mít tisíc pravidel ale jen ty opravdu unikátní, jsem holt panikář :-) ,  trochu jsem se toho obával ale máš pravdu, konfigy budou jen u větších projektů trochu ukecaný, ale jinak si myslím že budu potřebovat předávat  hlavně DbConnection apod, a to se dá jednoduše zautomatizovat, takže dík za vyřešení problému

Nahlásit jako SPAM
IP: 89.103.9.–
Kit+15
Guru
24. 12. 2015   #8
-
0
-

#7 panika
Na předávání připojení k databázi nepotřebuješ DIC. Sám to připojení ukládám do modelu a ten model předám každému controlleru nebo view, které vyrobím v továrně (faktorka je na můj vkus poněkud podivný pojem). Model si však připojení k databázi drží a nikomu ho nedá - funguje jako prostředník pro všechny služby. Tím se liší od služeb typu Register, které slouží pouze jako kontejnery.

K DI kontejnerům mám totiž docela vážné výhrady.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:207:e...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
ondrej39+1
Věrný člen
24. 12. 2015   #9
-
0
-

#8 Kit
Můžeš Kite uvést, co se ti konkrétně na DIC nelíbí? To by mě docela zajímalo. Při správném použití DIC pouze usnadňují práci, v PHP je například Dice, PHP-DI, v Googlu se používá pro Javu jejich vlastní Guice atp.

Jinak ony nakonec DIC nic jiného než chytré továrny nejsou, myslím si, že se od tvého manuálního přístupu moc neliší.

Nahlásit jako SPAM
IP: 46.39.172.–
Inject all the dependencies!
Kit+15
Guru
24. 12. 2015   #10
-
0
-

#9 ondrej39
Nelíbí se mi, že ty kontejnery samy o sobě nic nedělají. Ostatně i Registry je antipatternem právě z tohoto důvodu. Nedávám $db do $model proto, abych následně někde jinde udělal $model->getDb(). Tohle se mi prostě příčí, je to na můj vkus příliš anemické.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:207:e...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
ondrej39+1
Věrný člen
24. 12. 2015   #11
-
0
-

#10 Kit
Nemají ty DIC právě úlohu zkonstruovat ti konzistentní objektový graf v případě, že si vyžádáš určitou třídu? Tedy úplně to stejné, co dělají faktorky?

Jediný rozdíl, který mezi zautomatizovanými DIC a manuálními faktorkami vidím já, je ten, že faktorky prostě musíš napsat, zatímco zautomatizovaný DIC prostě zavoláš a máš třídu.

Ano, je zde jeden problém, že když si v projektu, kde používáš třeba Dice, vyhledáš si v celém projektu použití konkrétní třídy, tak ji prostě nikde nenajdeš a debugování je v takovém případě horší. Toto je jediný důvod, proč nemám zautomatizované DIC rád a raději si napíši vlastní generátor faktorek.

Nahlásit jako SPAM
IP: 46.39.172.–
Inject all the dependencies!
panika
~ Anonymní uživatel
6 příspěvků
24. 12. 2015   #12
-
0
-

#8 Kit
To je trochu neotřelý přístup ale podle tvých argumentů to je určitě anti getsetrický, což je dost čistý přístup, mohl by jsi mi prosím tě ukázat tvůj přístup na něčem krátkým?.

Nahlásit jako SPAM
IP: 89.103.9.–
Kit+15
Guru
24. 12. 2015   #13
-
0
-

#11 ondrej39
Jenže mechanismus DIC poněkud selhává při mockování - musíš pak mockovat celý DIC a to je dost nepohodlné.

Automatizovaný DIC také musíš napsat a nakonfigurovat. Problém jen přesouváš do jiné domény.

Nikdy po továrně nevyžaduji určitou třídu. Chci po ní objekt a je mi vcelku jedno, jaké bude třídy. Musí pouze splňovat definované rozhraní.

Debugování se raději vyhýbám, bývá to zbytečně časově náročné.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:207:e...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
ondrej39+1
Věrný člen
24. 12. 2015   #14
-
0
-

#13 Kit
Musíš mokovat DIC jen v případě, kdy ho používáš jako service locator, což samozřejmě dělat nemáš, to už jsem psal v #2. Když ho používáš tak, jak se používat má, tak nikdy žádná z tvých tříd o DIC nebude nic vědět a mockneš si pro unit testy jenom třídy, které opravdu potřebuješ (stejně jako v případě faktorek).

interface Service
{
    public function foo();
    public function bar();
}

final class SpecialService implements \Service
{
    public function foo() { return "SpecialFoo"; }
    public function bar() { return "SpecialBar"; }
}

final class ServiceFacade
{
    private $service;

    public function __construct(\Service $service)
    {
        $this->service = $service
    {

    public function fooBar()
    {
        return $this->service->foo().' '.$this->service->bar();
    }

    public function barFoo()
    {
        return $this->service->bar().' '.$this->service->foo();
    }
}

// Vyžaduje samozřejmě konfiguraci,
// která implementace Service se má použít
$dice->create('\ServiceFacade');

Nikde v tvé doméně ale žádná třída o DIC samozřejě neví. Pokud ví, tak je aplikace blbě napsaná a není to vina DIC, ale vývojářů, kteřá ho špatně používají.

Nahlásit jako SPAM
IP: 46.39.172.–
Inject all the dependencies!
Kit+15
Guru
24. 12. 2015   #15
-
0
-

#12 panika
Tak jednoduchý příklad líného otevření databáze, předání do modelu a volání modelu: 

<?php
$url = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];
$config = parse_ini_file($configFile , true);
$db = new MyPDO($config['MySQL']);
$model = new Model($db, $config);
$objekt = factory($model, $method, parseURL($url));
echo $objekt;

Tohle je vlastně obdoba konfigurace DIC, poslední řádek (jediné echo v celém projektu) vygeneruje výstup. Jestli $objekt je view či controller, toho nezajímá. To zajímá pouze funkci factory(), která to pozná podle druhého parametru.

MyPDO je třída, která si v konstruktoru zapamatuje parametry připojení k databázi, ale fyzicky ji otevře až ve chvíli, kdy model zavolá metodu $db->prepare() nebo $db->query(). Je podle vzoru Adaptér. Líné otevírání jsem použil proto, že u některých dotazů není volání DB vůbec potřebné a bylo by tedy zbytečné ji otevírat fyzicky.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:207:e...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Kit+15
Guru
24. 12. 2015   #16
-
0
-

#14 ondrej39
Netuším, co mají dělat metody foo(), bar(), barFoo() a fooBar(), takže se v tom tvém příkladu poněkud ztrácím. Rovněž mi nic neříká interface Service - z názvu vůbec není patrná žádná vlastnost rozhraní. Ten tvůj příklad neobsahuje žádnou sémantiku, je pro mne prakticky nečitelný.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:207:e...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
ondrej39+1
Věrný člen
24. 12. 2015   #17
-
0
-

#16 Kit
Ach jo. Prezentuji, že doménová vrstva nic neví o DIC, což jsi psal, že je jedna z vlastností, která ti na DIC vadí, a ty řešíš, že nevíš, co metody foo a bar dělají, ačkoliv na tom ale vůbec nezáleží. Kite, s tebou občas něco řešit, to je fakt na dlouho.

Líné otevírání jsem použil proto, že u některých dotazů není volání DB vůbec potřebné a bylo by tedy zbytečné ji otevírat fyzicky.

K tomuhle jen dodatek, pokud nějaký model databázi nepotřebuje, tak by ji ani neměl nikde v sobě mít. Pokud vkládáš databázi do modelu, který ji nepoužívá, děláš to špatně.

Nahlásit jako SPAM
IP: 46.39.172.–
Inject all the dependencies!
Kit+15
Guru
24. 12. 2015   #18
-
0
-

#17 ondrej39
Model databázi potřebuje, aby mohl reagovat na dotazy z controlleru a view. Nemůže za to, pokud se ho na nic nezeptají nebo pokud se ho zeptají na něco, co má model jinde než v databázi. Zpravidla tu databázi potřebují, s výjimkou vadného uživatelského vstupu, např. když útočník ručně přepíše URL.

Někteří na otevírání používají Singleton. Můžeš si myslet, že mám něco podobného, ale na rozdíl od běžné implementace si konfiguraci injektuji do konstruktoru. Připadá mi to mnohem čistější, než kdybych tu konfiguraci hledal někde v polovině běhu programu. Také mi to umožňuje injektovat úplně jinou databázi nebo mock.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:207:e...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
panika
~ Anonymní uživatel
6 příspěvků
24. 12. 2015   #19
-
0
-

#15 Kit
Aha, tvůj přístup mi rozhodně stojí za zamyšlení, hádám že nějaký tutoriál na internetu nenajdu, ale podle tvýho kusu kódu to do kupy dám.

Nahlásit jako SPAM
IP: 89.103.9.–
Kit+15
Guru
24. 12. 2015   #20
-
0
-

#19 panika
Je to jen implementace Dependency Injection bez kontejneru. Zatím jsem neměl tak složitý model, abych ten kontejner potřeboval. Dokonce i služby modelu volám přes Dependency Injection, ale to v příkladu nevidíš. Tím se mi celý model významně zkrátil na nějakých 36 řádek a stal se značně nezávislým na zbytku aplikace.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:207:e...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 10 hostů

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý