Imaginárny register php. Problém inicializácie objektov v OOP aplikáciách v PHP. Hľadanie riešenia pomocou registra, továrenskej metódy, lokátora služieb a vzorov vstrekovania závislostí

  • 20.06.2020

Po dotknutí sa štruktúry budúcej databázy. Začalo sa a nie je možné ustúpiť a ani na to nemyslím.

K databáze sa vrátime o niečo neskôr, ale zatiaľ sa pustíme do písania kódu pre náš engine. Najprv však trochu „materiálu“. Začať.

Začiatok času

Momentálne máme len nejaké predstavy a pochopenie fungovania systému, ktorý chceme implementovať, no k samotnej implementácii zatiaľ nedochádza. Nemáme s čím pracovať: chýba nám akákoľvek funkčnosť - a ako si pamätáte, rozdelili sme to na 2 časti: internú a externú. Písmená sú potrebné pre abecedu a interná funkčnosť je potrebná pre externú funkčnosť - začneme s ňou.

Ale nie tak rýchlo. Aby to fungovalo, musíte kopať trochu hlbšie. Náš systém predstavuje hierarchiu a každý hierarchický systém má začiatok: bod pripojenia v Linuxe, lokálny disk vo Windows, systém štátu, spoločnosť, vzdelávacia inštitúcia atď. Každý prvok takéhoto systému je niekomu podriadený a môže mať viacero podriadených a na oslovovanie svojich susedov a ich podriadených využíva nadriadený alebo samotný začiatok. Dobrým príkladom hierarchického systému je rodokmeň: vyberie sa východiskový bod – nejaký predok a ideme preč. V našom systéme potrebujeme aj záchytný bod, z ktorého nám vyrastú pobočky – moduly, pluginy atď. Potrebujeme nejaké rozhranie, cez ktoré budú všetky naše moduly „komunikovať“. Pre ďalšiu prácu sa musíme zoznámiť s konceptom „ dizajnový vzor" a pár ich implementácií.

Dizajnové vzory

Článkov o tom, čo to je a aké sú odrody, je veľmi veľa, téma je dosť otrepaná a nepoviem vám nič nové. Na vašej obľúbenej Wiki pre informácie o tejto téme, vagón so šmýkačkou a trochu viac.

Dizajnové vzory sa tiež často označujú ako dizajnové vzory alebo jednoducho vzory (z anglického slova pattern, čo znamená „vzor“). Ďalej v článkoch, keď už hovoríme o vzoroch, budem mať na mysli dizajnové vzory.

Z obrovského zoznamu všelijakých strašných (a nie tak) názvov vzorov nás zatiaľ zaujímajú len dva: register (registra) a singleton (singleton).

Registratúra (alebo sa zaregistrujte) je vzor, ​​ktorý funguje na poli, v ktorom môžete pridávať a odstraňovať určitú množinu objektov a pristupovať ku ktorémukoľvek z nich a jeho schopnostiam.

samotár (alebo singleton) je vzor, ​​ktorý zabezpečuje, že môže existovať iba jedna inštancia triedy. Nedá sa skopírovať, uspať ani zobudiť (hovoríme o PHP mágii: __clone(), __sleep(), __wakeup()). Singleton má globálny hotspot.

Definície nie sú úplné, zovšeobecnené, ale na pochopenie to stačí. Samostatne ich stále nepotrebujeme. Zaujímajú nás možnosti každého z týchto vzorov, ale v jednej triede: takýto vzor sa nazýva singleton register alebo Singleton register.

Čo nám to dá
  • Budeme mať zaručenú jedinú inštanciu registra, do ktorej môžeme kedykoľvek pridávať objekty a používať ich odkiaľkoľvek v kóde;
  • nebude možné ho skopírovať a použiť inú nechcenú (v tomto prípade) mágiu jazyka PHP.

V tejto fáze stačí pochopiť, že jeden register vám umožní implementovať modulárnu štruktúru systému, čo sme chceli, keď sme diskutovali o cieľoch v , a zvyšok pochopíte, ako vývoj napreduje.

Dosť bolo rečí, poďme byť kreatívni!

Prvé riadky

Keďže táto trieda bude súvisieť s funkcionalitou jadra, začneme vytvorením priečinka s názvom core v koreni nášho projektu, do ktorého umiestnime všetky triedy modulov jadra. Začneme registrom, takže súbor sa bude volať register.php

Nezaujíma nás možnosť, keď zvedavý používateľ zadá do riadku prehliadača priamu adresu nášho súboru, preto sa musíme pred tým chrániť. Na dosiahnutie tohto cieľa nám stačí v hlavnom spustiteľnom súbore zadefinovať určitú konštantu, ktorú budeme kontrolovať. Myšlienka nie je nová, bola použitá, pokiaľ si pamätám, v Joomle. Ide o jednoduchú a fungujúcu metódu, takže sa zaobídeme aj bez bicyklov.

Keďže chránime niečo pripojiteľné, konštantu budeme nazývať _PLUGSECURE_ :

If (!defined("_PLUGSECURE_")) ( die("Priame volanie modulu zakázané!"); )

Teraz, keď sa pokúsite získať prístup k tomuto súboru priamo, nič užitočné z toho nebude, čo znamená, že cieľ bol dosiahnutý.

Ďalej navrhujem stanoviť určitý štandard pre všetky naše moduly. Chcem poskytnúť každému modulu funkciu, ktorá o ňom vráti nejaké informácie, ako napríklad názov modulu, a táto funkcia by sa mala v triede vyžadovať. Na dosiahnutie tohto cieľa napíšeme nasledovné:

Rozhranie StorableObject ( verejná statická funkcia getClassName(); )

Páči sa ti to. Teraz, ak pripojíme akúkoľvek triedu bez funkcie getClassName() zobrazí sa nám chybové hlásenie. Zatiaľ sa tomu nebudem venovať, bude sa nám to hodiť neskôr, aspoň na testy a ladenie.

Je čas na hodinu nášho jednotného registra. Začneme deklarovaním triedy a niektorých jej premenných:

Class Registry implementuje StorableObject ( //meno modulu čitateľné private static $className = "Registry"; //inštancia registra private static $instance; //pole objektov private static $objects = array();

Zatiaľ čo všetko je logické a zrozumiteľné. Teraz, ako si pamätáte, máme register s vlastnosťami singleton, takže okamžite napíšeme funkciu, ktorá nám umožní pracovať s registrom týmto spôsobom:

Verejná statická funkcia singleton() ( if(!isset(self::$instance)) ( $obj = __CLASS__; self::$instance = new $obj; ) return self::$instance; )

Doslova: funkcia skontroluje, či inštancia nášho registra existuje: ak nie, vytvorí ju a vráti, ak už existuje, jednoducho sa vráti. V tomto prípade je pre nás nejaká mágia zbytočná, pre ochranu ju vyhlásime za súkromnú:

Súkromná funkcia __construct()() súkromná funkcia __clone()() súkromná funkcia __wakeup()() súkromná funkcia __sleep() ()

Teraz potrebujeme funkciu na pridanie objektu do nášho registra – takáto funkcia sa nazýva setter (setter) a rozhodol som sa ju implementovať dvoma spôsobmi, aby som ukázal, ako sa dá použiť mágia a poskytol alternatívny spôsob pridania objektu. Prvá metóda je štandardná funkcia, druhá metóda vykoná prvú pomocou kúzla __set().

//$object - cesta k pripojenému objektu //$key - prístupový kľúč k objektu v registri verejná funkcia addObject($key, $object) ( required_once($object); //vytvorenie objektu v poli objektov self::$objects[ $key] = new $key(self::$instance); ) //alternatívna metóda prostredníctvom magickej verejnej funkcie __set($key, $object) ( $this->addObject($key, $object ;)

Teraz na pridanie objektu do nášho registra môžeme použiť dva typy záznamov (povedzme, že sme už vytvorili inštanciu registra $registry a chceme pridať súbor config.php):

$registry->addObject("config", "/core/config.php"); //normálna metóda $registry->config = "/core/config.php"; //prostredníctvom magickej funkcie PHP __set()

Oba záznamy budú plniť rovnakú funkciu – zahrnú súbor, vytvoria inštanciu triedy a pomocou kľúča ju umiestnia do registra. Je tu jeden dôležitý bod, na ktorý by sme v budúcnosti nemali zabúdať: kľúč objektu v registri sa musí zhodovať s názvom triedy v obsiahnutom objekte. Ak sa znova pozriete na kód, pochopíte prečo.

Ktorý vstup použijete, je len na vás. Viac sa mi páči nahrávanie magickou metódou - je "krásne" a kratšie.

Takže sme prišli na pridanie objektu, teraz potrebujeme funkciu na prístup k pripojenému objektu pomocou kľúča - getter. Implementoval som to aj s dvoma funkciami, podobnými nastavovaču:

//získanie objektu z registra //$key - kľúč vo verejnej funkcii poľa getObject($key) ( //kontrola, či je premenná objektom if (is_object(self::$objects[$key])) ( //ak áno, potom vráťte tento objekt return self::$objects[$key]; ) ) //podobná metóda prostredníctvom magickej verejnej funkcie __get($key) ( if (is_object(self::$objects[$key) ])) ( return self: :$objects[$key]; ) )

Rovnako ako v prípade nastavovača, na získanie prístupu k objektu budeme mať 2 ekvivalentné položky:

$registry->getObject("config"); //normálna metóda $registry->config; //prostredníctvom magickej funkcie PHP __get()

Pozorný čitateľ si hneď položí otázku: prečo v magickej funkcii __set() len volám bežnú (nemagickú) funkciu pridania objektu a v getteri __get() skopírujem kód funkcie getObject() namiesto toho istého volania? Aby som bol úprimný, nemôžem na túto otázku odpovedať dostatočne presne, môžem len povedať, že som mal problémy pri práci s mágiou __get () v iných moduloch, ale pri prepisovaní kódu „na čelo“ takéto problémy nie sú.

Možno aj preto som sa v článkoch často stretával s výčitkami smerom k magickým metódam PHP a radami, ako sa ich vyhnúť.

"Všetka mágia má svoju cenu." © Rumplestiltskin

V tejto fáze je hotová hlavná funkcionalita nášho registra: môžeme vytvoriť jednu inštanciu registra, pridávať objekty a pristupovať k nim pomocou konvenčných metód aj magických metód jazyka PHP. "Ale čo odstránenie?"- zatiaľ túto funkciu nebudeme potrebovať a nie som si istý, či sa v budúcnosti niečo zmení. Nakoniec môžeme vždy pridať požadovanú funkcionalitu. Ale ak sa teraz pokúsime vytvoriť inštanciu nášho registra,

$registre = Register::singleton();

dostaneme chybu:

fatálna chyba: Register tried obsahuje 1 abstraktnú metódu, a preto musí byť vyhlásená za abstraktnú alebo implementovať zostávajúce metódy (StorableObject::getClassName) v …

Všetko preto, že sme zabudli napísať požadovanú funkciu. Pamätáte si, že som na začiatku hovoril o funkcii, ktorá vracia názov modulu? Tu je to na plný výkon a ešte treba dodať. Je jednoduchá:

verejná statická funkcia getClassName() ( return self::$className; )

Teraz by nemali byť žiadne chyby. Navrhujem pridať ešte jednu funkciu, nie je povinná, ale skôr či neskôr sa môže hodiť, v budúcnosti ju využijeme na kontroly a ladenie. Funkcia vráti názvy všetkých objektov (modulov) pridaných do nášho registra:

Verejná funkcia getObjectsList() ( //pole na vrátenie $names = array(); //získanie názvu každého objektu z poľa objektov foreach(self::$objects as $obj) ( $names = $obj- >getClassName() ; ) //pridať názov modulu registra do poľa array_push($names, self::getClassName()); //a vrátiť návrat $names; )

To je všetko. Tým je register hotový. Poďme to skontrolovať, dobre? Pri kontrole budeme musieť niečo pripojiť - nech je to konfiguračný súbor. Vytvorte nový súbor core/config.php a pridajte doň minimálny obsah požadovaný naším registrom:

//nezabudnite skontrolovať konštantu if (!defined("_PLUGSECURE_")) ( die("Priame volanie modulu je zakázané!"); ) class Config ( //názov modulu čitateľný súkromný statický $className = "Config" ; verejná statická funkcia getClassName() ( return self::$className; ) )

Niečo také. Teraz poďme k samotnému testu. V koreňovom adresári nášho projektu vytvorte súbor index.php a napíšte doň nasledujúci kód:

Define("_PLUGSECURE_", true); //definovať konštantu na ochranu pred priamym prístupom k objektom require_once "/core/registry.php"; //pripojeny register $registre = Registry::singleton(); //vytvorila sa inštancia jedného registra $registry->config = "/core/config.php"; //pripájame našu, zatiaľ zbytočnú, konfiguráciu //zobrazenie mien pripojených modulov echo " Pripojené"; foreach($registry->

  • " . $names ."
  • "; }

    Alebo, ak sa stále vyhýbate mágii, 5. riadok možno nahradiť alternatívnou metódou:

    Define("_PLUGSECURE_", true); //definovať konštantu na ochranu pred priamym prístupom k objektom require_once "/core/registry.php"; //pripojeny register $registre = Registry::singleton(); //vytvorila sa inštancia jedného registra $registry->addObject("config", "/core/config.php"); //pripájame našu, zatiaľ zbytočnú, konfiguráciu //zobrazenie mien pripojených modulov echo " Pripojené"; foreach ($registry->getObjectsList() ako $names) ( echo "

  • " . $names ."
  • "; }

    Teraz otvoríme prehliadač a do panela s adresou napíšeme adresu http://localhost/index.php alebo len http://localhost/ (relevantné, ak používate štandardné nastavenia Open Server alebo podobného webového servera)

    V dôsledku toho by sme mali vidieť niečo takéto:

    Ako vidíte, neexistujú žiadne chyby, čo znamená, že všetko funguje, k čomu vám blahoželám 🙂

    Dnes sa tam zastavíme. V ďalšom článku sa vrátime k databáze a napíšeme si triedu pre prácu s databázou MySQL, pripojíme ju k registrom a skontrolujeme prácu v praxi. Maj sa!

    Problém inicializácie objektov v OOP aplikáciách v PHP. Hľadanie riešenia pomocou registra, továrenskej metódy, lokátora služieb a vzorov vstrekovania závislostí

    Stáva sa, že programátori opravia úspešné riešenia v podobe návrhových vzorov. Existuje veľa literatúry o šablónach. Kniha Gang of Four „Design Patterns“ od Ericha Gammu, Richarda Helma, Ralpha Johnsona a Johna Vlissidesa“ a možno aj „Patterns of Enterprise Application Architecture“ od Martina Fowlera je určite považovaná za klasiku. To najlepšie, čo som s PHP čítal príklady - toto. Náhodou je všetka táto literatúra dosť komplikovaná pre ľudí, ktorí sa práve začínajú učiť OOP. Tak ma napadlo rozložiť niektoré vzory, ktoré považujem za najužitočnejšie, veľmi zjednodušeným spôsobom. slovami, tento článok je mojím prvým pokusom o interpretáciu dizajnových vzorov v štýle KISS.
    Dnes si povieme, aké problémy môžu nastať pri inicializácii objektov v OOP aplikácii a ako môžete použiť niektoré populárne návrhové vzory na vyriešenie týchto problémov.

    Príklad

    Moderná OOP aplikácia pracuje s desiatkami, stovkami a niekedy aj tisíckami objektov. Poďme sa teda bližšie pozrieť na to, ako sú tieto objekty inicializované v našich aplikáciách. Inicializácia objektu je jediný aspekt, ktorý nás v tomto článku zaujíma, preto som sa rozhodol vynechať všetku implementáciu „navyše“.
    Povedzme, že sme vytvorili super-duper užitočnú triedu, ktorá dokáže odoslať požiadavku GET na konkrétny URI a vrátiť HTML z odpovede servera. Aby naša trieda nepôsobila príliš jednoducho, nech skontroluje aj výsledok a v prípade „nesprávnej“ odpovede servera vyvolá výnimku.

    Class Grabber ( verejná funkcia get($url) (/** vráti HTML kód alebo vyvolá výnimku */) )

    Vytvorme ďalšiu triedu, ktorej objekty budú zodpovedné za filtrovanie prijatého HTML. Metóda filtra berie ako argumenty kód HTML a selektor CSS a vracia pole prvkov nájdených daným selektorom.

    Class HtmlExtractor ( filter verejnej funkcie ($html, $selector) (/** vráti pole filtrovaných prvkov */) )

    Teraz si predstavme, že potrebujeme získať výsledky vyhľadávania Google pre dané kľúčové slová. Za týmto účelom predstavíme ďalšiu triedu, ktorá použije triedu Grabber na odoslanie požiadavky a triedu HtmlExtractor na extrakciu potrebného obsahu. Bude obsahovať aj logiku na zostavenie URI, selektor na filtrovanie výsledného HTML a spracovanie výsledkov.

    Trieda GoogleFinder ( private $grabber; private $filter; verejná funkcia __construct() ( $this->grabber = new Grabber(); $this->filter = new HtmlExtractor(); ) verejná funkcia find($searchString) ( /* * vráti pole založených výsledkov */) )

    Všimli ste si, že inicializácia objektov Grabber a HtmlExtractor je v konštruktore triedy GoogleFinder? Zamyslime sa nad tým, aké dobré je toto riešenie.
    Samozrejme, napevno zakódovať vytváranie objektov v konštruktore nie je dobrý nápad. A preto. Po prvé, nemôžeme jednoducho prepísať triedu Grabber v testovacom prostredí, aby sme sa vyhli odoslaniu skutočnej požiadavky. Aby sme boli spravodliví, dá sa to urobiť pomocou rozhrania Reflection API . Tie. existuje technická možnosť, ale toto nie je ani zďaleka najpohodlnejší a najzrejmejší spôsob.
    Po druhé, rovnaký problém nastane, ak chceme znova použiť logiku GoogleFinder s inými implementáciami Grabber a HtmlExtractor. Vytvorenie závislosti je pevne zakódované v konštruktore triedy. A v najlepšom prípade budeme môcť zdediť GoogleFinder a prepísať jeho konštruktor. A aj to len vtedy, ak je rozsah vlastností grabberu a filtra chránený alebo verejný.
    A posledný bod, pri každom vytvorení nového objektu GoogleFinder sa v pamäti vytvorí nový pár objektov závislosti, aj keď jeden objekt typu Grabber a jeden objekt typu HtmlExtractor môžeme použiť vo viacerých objektoch typu GoogleFinder.
    Myslím, že ste už pochopili, že inicializáciu závislostí je potrebné presunúť mimo triedy. Môžeme vyžadovať, aby sa už pripravené závislosti odovzdali konštruktorovi triedy GoogleFinder.

    Trieda GoogleFinder ( private $grabber; private $filter; public function __construct(Grabber $grabber, HtmlExtractor $filter) ( $this->grabber = $grabber; $this->filter = $filter; ) verejná funkcia find($searchString) ( /** vráti pole založených výsledkov */) )

    Ak chceme dať ostatným vývojárom možnosť pridávať a používať ich implementácie Grabber a HtmlExtractor, potom by sme mali zvážiť zavedenie rozhraní pre nich. V tomto prípade je to nielen užitočné, ale aj nevyhnutné. Domnievam sa, že ak v projekte použijeme len jednu implementáciu a nemienime v budúcnosti vytvárať nové, tak by sme od tvorby rozhrania mali upustiť. Je lepšie konať podľa situácie a urobiť jednoduchý refaktoring, keď je to skutočne potrebné.
    Teraz máme všetky požadované triedy a v ovládači môžeme použiť triedu GoogleFinder.

    Class Controller ( public function action() ( /* Some things */ $finder = new GoogleFinder(new Grabber(), new HtmlExtractor()); $results = $finder->

    Zhrňme si priebežný výsledok. Napísali sme pomerne veľa kódu a na prvý pohľad sme neurobili nič zlé. Ale... čo ak potrebujeme použiť objekt ako GoogleFinder inde? Jeho vytvorenie budeme musieť duplikovať. V našom príklade je to len jeden riadok a problém nie je taký nápadný. V praxi môže byť inicializácia objektov pomerne zložitá a môže trvať až 10 riadkov, prípadne aj viac. Existujú aj ďalšie problémy typické pre duplikáciu kódu. Ak počas refaktoringu potrebujete zmeniť názov použitej triedy alebo logiku inicializácie objektov, budete musieť manuálne zmeniť všetky miesta. myslim ze vies ako to chodi :)
    S pevným kódom sa zvyčajne ľahko zaobchádza. V konfigurácii sa spravidla vyberú duplicitné hodnoty. To umožňuje centrálne meniť hodnoty na všetkých miestach, kde sa používajú.

    Šablóna registra.

    Rozhodli sme sa teda presunúť vytváranie objektov do konfigurácie. Poďme to urobiť.

    $registre = new ArrayObject(); $registry["grabber"] = new Grabber(); $registry["filter"] = new HtmlExtractor(); $registry["google_finder"] = nový GoogleFinder($registry["grabber"], $registry["filter"]);
    Musíme len odovzdať náš ArrayObject ovládaču a problém je vyriešený.

    Class Controller ( súkromný $registre; verejná funkcia __construct(ArrayObject $registry) ( $this->registre = $registry; ) verejná funkcia action() ( /* Niektoré veci */ $results = $this->registre["google_finder" ]->find("hľadaný reťazec"); /* Urobte niečo s výsledkami */ ) )

    Môžete ďalej rozvíjať myšlienku registra. Zdediť ArrayObject, zapuzdreť vytváranie objektov v novej triede, zabrániť pridávaniu nových objektov po inicializácii atď. Podľa môjho názoru však vyššie uvedený kód úplne objasňuje, čo je šablóna databázy Registry. Tento vzorec nie je generatívny, ale do určitej miery rieši naše problémy. Register je len kontajner, do ktorého môžeme ukladať objekty a prenášať ich v rámci aplikácie. Aby boli objekty dostupné, musíme ich najskôr vytvoriť a zaregistrovať v tomto kontajneri. Poďme sa pozrieť na výhody a nevýhody tohto prístupu.
    Na prvý pohľad sme svoj cieľ splnili. Prestali sme pevne kódovať názvy tried a vytvárať objekty na jednom mieste. Objekty vytvárame v jedinej inštancii, čo zaručuje ich opätovné použitie. Ak sa zmení logika vytvárania objektov, potom bude potrebné upraviť iba jedno miesto v aplikácii. Ako bonus sme dostali možnosť centrálne spravovať objekty v Registri. Môžeme ľahko získať zoznam všetkých dostupných objektov a vykonať s nimi nejaké manipulácie. Poďme sa teraz pozrieť, čo by nám na tejto šablóne nemuselo vyhovovať.
    Najprv musíme vytvoriť objekt pred jeho registráciou v registri. Podľa toho je vysoká pravdepodobnosť vytvárania „zbytočných predmetov“, t.j. tie, ktoré sa vytvoria v pamäti, no v aplikácii sa nepoužijú. Áno, objekty môžeme pridávať do Registra dynamicky, t.j. vytvorte len tie objekty, ktoré sú potrebné na spracovanie konkrétnej požiadavky. Tak či onak, budeme ho musieť ovládať manuálne. Preto bude časom veľmi ťažké ho udržiavať.
    Po druhé, máme novú závislosť na ovládači. Áno, objekty môžeme získať prostredníctvom statickej metódy v registri, aby sme neprešli register konštruktorovi. Ale podľa mňa by sa to nemalo robiť. Statické metódy, to je ešte pevnejšie spojenie ako vytváranie závislostí v rámci objektu a ťažkosti pri testovaní (tu o tejto téme).
    Po tretie, rozhranie ovládača nám nehovorí nič o tom, aké objekty používa. Do ovládača môžeme dostať akýkoľvek objekt dostupný v Registri. Bude pre nás ťažké presne povedať, aké objekty ovládač používa, kým neskontrolujeme celý jeho zdrojový kód.

    Továrenská metóda

    Naša najväčšia sťažnosť na register je, že objekt musí byť inicializovaný predtým, ako bude dostupný. Namiesto inicializácie objektu v konfigurácii môžeme oddeliť logiku vytvárania objektov do inej triedy, z ktorej môžeme „požiadať“ o zostavenie objektu, ktorý potrebujeme. Triedy, ktoré sú zodpovedné za vytváranie objektov, sa nazývajú továrne. A návrhový vzor sa nazýva Factory Method. Pozrime sa na továrenský príklad.

    Class Factory ( verejná funkcia getGoogleFinder() ( return new GoogleFinder($this->getGrabber(), $this->getHtmlExtractor()); ) súkromná funkcia getGrabber() ( return new Grabber(); ) súkromná funkcia getHtmlExtractor() ( vrátiť nový HtmlFiletr(); ) )

    Spravidla sa vyrábajú továrne, ktoré sú zodpovedné za vytvorenie jedného typu objektu. Niekedy môže továreň vytvoriť skupinu súvisiacich objektov. Môžeme použiť ukladanie vlastností do vyrovnávacej pamäte, aby sme sa vyhli opätovnému vytváraniu objektov.

    Class Factory ( súkromné ​​$finder; verejná funkcia getGoogleFinder() ( if (null === $this->finder) ( $this->finder = new GoogleFinder($this->getGrabber(), $this->getHtmlExtractor() ); ) vrátiť $this->finder; ) )

    Môžeme parametrizovať továrenskú metódu a delegovať inicializáciu na iné továrne v závislosti od prichádzajúceho parametra. Toto už bude vzor Abstract Factory.
    Ak bude potrebné rozdeliť aplikáciu na moduly, môžeme požadovať, aby každý modul poskytoval svoje vlastné továrne. Môžeme pokračovať v rozvíjaní témy tovární, ale myslím si, že podstata tohto vzoru je jasná. Pozrime sa, ako budeme používať továreň v ovládači.

    Class Controller ( private $factory; verejná funkcia __construct(Factory $factory) ( $this->factory = $factory; ) verejná funkcia action() ( /* Niektoré veci */ $results = $this->factory->getGoogleFinder( )->find("hľadaný reťazec"); /* Urobte niečo s výsledkami */ ) )

    Výhodou tohto prístupu je jeho jednoduchosť. Naše objekty sú vytvorené explicitne a vaše IDE vás jednoducho dovedie na miesto, kde sa to stane. Vyriešili sme aj problém s Registry a objekty v pamäti sa vytvoria až vtedy, keď o to „požiadame“ ​​továreň. Ale ešte sme sa nerozhodli, ako dodať potrebné továrne kontrolórom. Tu je viacero možností. Môžete použiť statické metódy. Môžeme nechať kontrolórov, aby si sami vytvorili potrebné továrne a zrušiť všetky naše pokusy zbaviť sa kopírovania. Môžete vytvoriť továreň na továrne a odovzdať ju iba kontrolórovi. Ale dostať objekty do ovládača bude trochu zložitejšie a bude potrebné riadiť závislosti medzi továrňami. Navyše nie je úplne jasné, čo robiť, ak chceme moduly využívať v našej aplikácii, ako registrovať továrne modulov, ako spravovať prepojenia medzi továrňami z rôznych modulov. Vo všeobecnosti sme stratili hlavnú výhodu továrne - explicitné vytváranie objektov. A doteraz nebol vyriešený problém „implicitného“ rozhrania ovládača.

    Vyhľadávač služieb

    Vzor Service Locator vám umožňuje vyriešiť chýbajúcu fragmentáciu tovární a riadiť vytváranie objektov automaticky a centrálne. Ak sa nad tým zamyslíme, môžeme zaviesť ďalšiu vrstvu abstrakcie, ktorá bude zodpovedná za vytváranie objektov v našej aplikácii a riadenie vzťahov medzi týmito objektmi. Aby nám táto vrstva mohla vytvárať objekty, budeme jej musieť dať vedomosti, ako na to.
    Podmienky šablóny lokátora služieb:
    • Služba (Service) - hotový predmet, ktorý je možné získať z kontajnera.
    • Definícia služby - logika inicializácie služby.
    • Kontajner (Service Container) je centrálny objekt, ktorý ukladá všetky popisy a dokáže z nich vytvárať služby.
    Každý modul môže zaregistrovať svoje popisy služieb. Ak chcete získať nejakú službu z kontajnera, budeme o ňu musieť požiadať kľúčom. Možností implementácie Service Locator je veľa, v najjednoduchšom prípade môžeme použiť ArrayObject ako kontajner a uzáver ako popis služieb.

    Class ServiceContainer rozširuje ArrayObject ( verejná funkcia get($key) ( if (is_callable($this[$key])) ( return call_user_func($this[$key]); ) vyvoláva novú \RuntimeException("Nedá sa nájsť definícia služby pod kľúč [ $key ]"); ))

    Potom bude registrácia definícií vyzerať takto:

    $kontajner = new ServiceContainer(); $container["grabber"] = funkcia () ( return new Grabber(); ); $container["html_filter"] = funkcia () ( return new HtmlExtractor(); ); $container["google_finder"] = function() use ($container) ( return new GoogleFinder($container->get("grabber"), $container->get("html_filter")); );

    A použite v ovládači takto:

    Class Controller ( private $container; public function __construct(ServiceContainer $container) ( $this->container = $container; ) public function action() ( /* Some things */ $results = $this->container->get( "google_finder")->find("hľadaný reťazec"); /* Urobte niečo s výsledkami */ ) )

    Servisný kontajner môže byť veľmi jednoduchý alebo môže byť veľmi zložitý. Napríklad Symfony Service Container poskytuje množstvo funkcií: parametre (parametre), rozsahy služieb (rozsahy), vyhľadávanie služieb podľa značiek (tagov), aliasov (aliasov), súkromné ​​služby (súkromné ​​služby), možnosť vykonávať zmeny kontajner po pridaní všetkých služieb (priechody kompilátora) a oveľa viac. DIExtraBundle ešte viac rozširuje štandardnú implementáciu.
    Ale späť k nášmu príkladu. Ako vidíte, Service Locator nielenže rieši všetky rovnaké problémy ako predchádzajúce šablóny, ale tiež vám umožňuje jednoducho používať moduly s vašimi vlastnými definíciami služieb.
    Okrem toho sme na rámcovej úrovni dostali ďalšiu úroveň abstrakcie. Totiž zmenou metódy ServiceContainer::get môžeme napríklad nahradiť objekt proxy. A rozsah proxy objektov je obmedzený iba fantáziou vývojára. Tu môžete implementovať paradigmu AOP a LazyLoading atď.
    Väčšina vývojárov však stále považuje Service Locator za anti-vzor. Pretože teoreticky môžeme mať toľko tzv. Triedy Container Aware (t. j. tie triedy, ktoré obsahujú odkaz na kontajner). Napríklad náš Controller, v rámci ktorého môžeme získať akúkoľvek službu.
    Pozrime sa, prečo je to zlé.
    Po prvé, opäť testovanie. Namiesto zosmiešňovania iba použitých tried v testoch budete musieť zosmiešniť celý kontajner alebo použiť skutočný kontajner. Prvá možnosť nevyhovuje, pretože. musíte v testoch napísať veľa zbytočného kódu, druhý, pretože ide proti zásadám testovania jednotiek a môže viesť k dodatočnej réžii na udržiavanie testov.
    Po druhé, bude pre nás ťažké refaktorovať. Zmenou akejkoľvek služby (alebo ServiceDefinition) v kontajneri budeme nútení skontrolovať aj všetky závislé služby. A táto úloha nie je vyriešená pomocou IDE. Nájsť takéto miesta v celej aplikácii nebude také jednoduché. Okrem závislých služieb budete musieť skontrolovať aj všetky miesta, kde sa refaktorovaná služba získava z kontajnera.
    No a tretím dôvodom je, že nekontrolované vyťahovanie služieb z kontajnera skôr či neskôr povedie k neporiadku v kóde a zbytočnému zmätku. Je ťažké to vysvetliť, len musíte stráviť viac a viac času, aby ste pochopili, ako konkrétna služba funguje, inými slovami, môžete úplne pochopiť, čo trieda robí alebo ako funguje, iba ak si prečítate celý jej zdrojový kód.

    Injekcia závislosti

    Čo ešte možno urobiť na obmedzenie používania kontajnera v aplikácii? Do frameworku je možné preniesť kontrolu nad vytváraním všetkých vlastných objektov, vrátane ovládačov. Inými slovami, používateľský kód by nemal volať metódu get kontajnera. V našom príklade môžeme do kontajnera Definition pre ovládač pridať:

    $container["google_finder"] = function() use ($container) ( return new Controller(Grabber $grabber); );

    A zbavte sa kontajnera v ovládači:

    Ovládač triedy ( súkromný $finder; verejná funkcia __construct(GoogleFinder $finder) ( $this->finder = $finder; ) verejná funkcia action() ( /* Niektoré veci */ $results = $this->finder->find( "hľadaný reťazec"); /* Urobte niečo s výsledkami */ ) )

    Tento prístup (keď nie je poskytovaný prístup ku kontajneru služieb klientskym triedam) sa nazýva Dependency Injection. Ale tento vzor má výhody aj nevýhody. Pokiaľ rešpektujeme princíp jedinej zodpovednosti, kódex vyzerá veľmi pekne. V prvom rade sme sa zbavili kontajnera v triedach klientov, vďaka čomu je ich kód oveľa prehľadnejší a jednoduchší. Radič môžeme jednoducho otestovať zmenou potrebných závislostí. Každú triedu môžeme vytvoriť a otestovať nezávisle (vrátane tried radičov) pomocou prístupu TDD alebo BDD. Pri vytváraní testov môžeme abstrahovať z kontajnera a neskôr pridať Definíciu, keď potrebujeme použiť konkrétne inštancie. Vďaka tomu bude náš kód jednoduchší a prehľadnejší a testovanie transparentnejšie.
    Treba však spomenúť aj rubovú stranu mince. Faktom je, že ovládače sú veľmi špecifické triedy. Začnime tým, že kontrolór spravidla obsahuje súbor úkonov, čo znamená, že porušuje zásadu jedinej zodpovednosti. V dôsledku toho môže mať trieda radiča oveľa viac závislostí, ako je potrebné na vykonanie konkrétnej akcie. Použitie lenivej inicializácie (objekt sa vytvorí pri prvom použití a predtým sa použije odľahčený proxy) do určitej miery rieši problém s výkonom. Ale z architektonického hľadiska vytváranie množstva závislostí na ovládači tiež nie je úplne správne. Navyše testovanie ovládačov je zvyčajne zbytočná operácia. Všetko, samozrejme, závisí od toho, ako je testovanie vo vašej aplikácii zorganizované a ako sa v ňom cítite vy.
    Z predchádzajúceho odseku ste pochopili, že použitie Dependency Injection úplne neodstráni problémy s architektúrou. Zamyslite sa preto nad tým, ako vám bude pohodlnejšie, či odkaz na kontajner uložiť do ovládačov alebo nie. Neexistuje jediné správne riešenie. Myslím si, že oba prístupy sú dobré, pokiaľ je kód ovládača jednoduchý. Rozhodne by ste však nemali vytvárať služby Conatiner Aware okrem ovládačov.

    závery

    Teraz je čas vyradiť všetko, čo bolo povedané. Veľa sa toho popísalo... :)
    Takže na štruktúrovanie práce pri vytváraní objektov môžeme použiť nasledujúce vzory:
    • Registratúra: Vzor má zjavné nevýhody, z ktorých najzákladnejšou je potreba vytvoriť predmety pred ich vložením do spoločného kontajnera. Je zrejmé, že z jeho používania budeme mať viac problémov ako výhod. Toto zjavne nie je najlepšie využitie vzoru.
    • Továrenská metóda: Hlavná výhoda vzoru: objekty sú vytvorené explicitne. Hlavnou nevýhodou je, že radiče sa buď musia starať o to, aby si vytvorili továrne sami, čo problém s hardcode názvom triedy úplne nerieši, alebo musí byť framework zodpovedný za zásobovanie ovládačov všetkými potrebnými továrňami, čo už nebude také samozrejmé. Chýba tu možnosť centrálne riadiť proces vytvárania objektov.
    • Vyhľadávač služieb: „Pokročilejší“ spôsob ovládania vytvárania objektov. Dodatočnú úroveň abstrakcie možno použiť na automatizáciu bežných úloh, s ktorými sa stretávame pri vytváraní objektov. Napríklad:
      class ServiceContainer rozširuje ArrayObject ( verejná funkcia get($key) ( if (is_callable($this[$key])) ( $obj = call_user_func($this[$key]); if ($obj instanceof RequestAwareInterface) ( $obj- >setRequest($this->get("request")); ) vrátiť $obj; ) vyvolať novú \RuntimeException("Nedá sa nájsť definícia služby pod kľúčom [ $key ]"); ) )
      Nevýhodou Service Locator je, že verejné API tried už nie je informatívne. Musíte si prečítať celý kód triedy, aby ste pochopili, aké služby používa. Trieda, ktorá obsahuje odkaz na kontajner, sa testuje ťažšie.
    • Injekcia závislosti: V zásade môžeme použiť rovnaký kontajner služieb ako v predchádzajúcom vzore. Rozdiel je v tom, ako sa tento kontajner používa. Ak sa vyhneme tomu, aby boli triedy závislé od kontajnera, získame jasné a explicitné API triedy.
    To nie je všetko, o čom by som chcel hovoriť o probléme vytvárania objektov v aplikáciách PHP. Existuje aj vzor Prototype, neuvažovali sme o použití Reflection API, ponechali sme bokom problém lenivého načítania služieb a mnohé ďalšie nuansy. Článok sa ukázal byť nemalý, takže zaokrúhľujem :)
    Chcel som ukázať, že Dependency Injection a iné vzorce nie sú také zložité, ako sa bežne verí.
    Ak hovoríme o Dependency Injection, potom existujú napríklad aj implementácie tohto vzoru KISS

    Tento vzor, ​​podobne ako Singleton, len zriedka spôsobuje pozitívnu reakciu vývojárov, pretože spôsobuje rovnaké problémy pri testovaní aplikácií. Napriek tomu nadávajú, ale aktívne to využívajú. Podobne ako Singleton , vzor Registry sa nachádza v mnohých aplikáciách a tak či onak výrazne zjednodušuje riešenie niektorých úloh.

    Zvážme obe možnosti v poradí.

    To, čo sa nazýva „čistý register“ alebo jednoducho register, je implementácia triedy so statickým rozhraním. Hlavným rozdielom oproti vzoru Singleton je zablokovanie možnosti vytvorenia aspoň jednej inštancie triedy. Vzhľadom na to nemá zmysel skrývať magické metódy __clone() a __wakeup() za súkromný alebo chránený modifikátor.

    Registratúrna trieda musí mať dve statické metódy – getter a setter. Setter umiestni odovzdaný objekt do úložiska viazaného na daný kľúč. Getter, respektíve, vracia objekt z úložiska. Obchod nie je nič iné ako pár kľúč – hodnota asociatívneho poľa.

    Pre plnú kontrolu nad registrom je zavedený ďalší prvok rozhrania – metóda, ktorá umožňuje vymazať objekt z úložiska.

    Okrem problémov identických so vzorom Singleton vynikajú dva ďalšie:

    • zavedenie iného typu závislosti - na kľúčoch registra;
    • dva rôzne kľúče databázy Registry môžu odkazovať na rovnaký objekt

    V prvom prípade nie je možné vyhnúť sa ďalšej závislosti. Do istej miery sme skutočne pripútaní k menám kľúčov.

    Druhý problém je vyriešený zavedením kontroly do metódy Registry::set():

    Verejná sada statických funkcií($key, $item) ( if (!array_key_exists($key, self::$_registry)) (foreach (self::$_registry ako $val) ( if ($val === $item) ( throw new Exception("Položka už existuje"); ) ) self::$_registry[$key] = $item; ) )

    « Vyčistite register vzorov“ vyvoláva ďalší problém – posilnenie závislosti kvôli potrebe odkazovať na nastavovača a získavateľa prostredníctvom názvu triedy. Nemôžete vytvoriť odkaz na objekt a pracovať s ním, ako v prípade vzoru Loner, keď bol tento prístup k dispozícii:

    $instance = Singleton::getInstance(); $instance->Foo();

    Tu máme možnosť uložiť referenciu na inštanciu Singleton napríklad do vlastnosti aktuálnej triedy a pracovať s ňou tak, ako to vyžaduje ideológia OOP: odovzdať ju ako parameter agregovaným objektom alebo použiť v potomkoch.

    Na vyriešenie tohto problému existuje Implementácia registra Singleton, ktorý sa mnohým nepáči, pretože je nadbytočný, ako si myslia kód. Myslím si, že dôvodom tohto postoja je určité nepochopenie princípov OOP alebo ich zámerné ignorovanie.

    _registre[$kľúč] = $objekt; ) statická verejná funkcia get($key) ( return self::getInstance()->_registry[$key]; ) súkromná funkcia __wakeup() ( ) súkromná funkcia __construct() ( ) súkromná funkcia __clone() ( ) ) ?>

    Aby som ušetril peniaze, zámerne som vynechal bloky komentárov pre metódy a vlastnosti. Myslím, že nie sú potrebné.

    Ako som už povedal, zásadný rozdiel je v tom, že teraz je možné uložiť odkaz na objem registra a nepoužívať zakaždým ťažkopádne volania statických metód. Táto možnosť sa mi zdá správnejšia. Súhlas alebo nesúhlas s mojím názorom nemá veľký význam, rovnako ako môj názor samotný. Žiadne jemnosti implementácie nezachránia vzor pred množstvom uvedených nevýhod.

    Pokúsim sa povedať o mojej implementácii vzoru databázy Registry pod php. Register je náhrada globálnej premennej OOP na ukladanie údajov a ich odovzdávanie medzi modulmi systému. V súlade s tým je vybavený štandardnými vlastnosťami - písanie, čítanie, mazanie. Tu je typická implementácia.

    Takto získame hlúpu náhradu metód $key = $value - Registry::set($key, $value) $key - Registry::get($key) unset($key) - odstrániť Registry:: remove($key ) Len to bude nejasné - prečo tento extra kód. Naučme teda našu triedu robiť to, čo globálne premenné nedokážu. Pridáme k tomu korenie.

    getMessage()); ) Amdy_Registry::unlock("test"); var_dump(Amdy_Registry::get("test")); ?>

    K typickým úlohám vzoru som pridal možnosť uzamknúť premennú pred zmenami, čo je veľmi výhodné na veľkých projektoch, nič tam náhodne nevložíte. Vhodná je napríklad práca s databázou
    define('DB_DNS', 'mysql:host=localhost;dbname= ’);
    define('DB_USER', ' ’);
    define('DB_PASSWORD', ' ’);
    define('DB_HANDLE');

    Amdy_Regisrtry::set(DB_HANDLE, nové PDO(DB_DNS, DB_USER, DB_PASSWORD));
    Amdy_Registry::lock(DB_HANDLE);

    Teraz vysvetlenia ku kódu, na ukladanie údajov používame statickú premennú $data, premenná $lock ukladá údaje o kľúčoch, ktoré sú uzamknuté na úpravu. V seteri skontrolujeme, či je premenná uzamknutá a zmeníme alebo pridáme do registra. Pri mazaní kontrolujeme aj zámok, geter zostáva nezmenený, okrem voliteľného predvoleného parametra. No, mali by ste si dať pozor na spracovanie výnimiek, ktoré sa z nejakého dôvodu používa málo, mimochodom, návrh výnimiek už mám, počkajte na článok. Trochu nižšie je návrh kódu na testovanie, tu je článok o testovaní, tiež by nezaškodilo čmárať, aj keď nie som obdivovateľom TDD.

    V ďalšom článku funkcionalitu ďalej rozšírime o inicializáciu dát a implementáciu „lenivosti“.

    Rozhodol som sa stručne napísať o vzoroch často používaných v našom živote, viac príkladov, menej vody, poďme na to.

    Singleton

    Hlavnou pointou „samotára“ je, že keď poviete „Potrebujem telefónnu ústredňu“, povie sa vám „Už je to tam postavené“, a nie „Poďme to postaviť znova“. „Single“ je vždy sám.

    Class Singleton ( private static $instance = null; súkromná funkcia __construct()( /* ... @return Singleton */ ) // Ochrana pred vytvorením pomocou novej súkromnej funkcie Singleton __clone() ( /* ... @return Singleton * / ) // Ochrana pred vytvorením prostredníctvom klonovania súkromnej funkcie __wakeup() ( /* ... @return Singleton */ ) // Ochrana pred vytvorením prostredníctvom zrušenia serializácie verejnej statickej funkcie getInstance() ( if (is_null(self::$instance) ) ( self::$instance = nové ja; ) return self::$instance; ) )

    Matrika (registra, zápisy do denníka)

    Ako už názov napovedá, tento vzor je určený na ukladanie záznamov, ktoré sú v ňom umiestnené, a podľa toho na vrátenie týchto záznamov (podľa názvu), ak sú potrebné. V príklade telefónnej ústredne ide o registratúru vo vzťahu k telefónnym číslam obyvateľov.

    Register triedy ( súkromný $registre = pole(); sada verejných funkcií ($kľúč, $objekt) ( $tento->registre[$kľúč] = $objekt; ) verejná funkcia get($kľúč) ( návrat $tento->registre [$key]; ) )

    Singleton Register (jediný register)- nezamieňať s)

    „Matrika“ je často „samotárka“, no nie vždy to tak musí byť. Napríklad v účtovníctve môžeme spustiť niekoľko denníkov, v jednom zamestnancovi od „A“ po „M“, v druhom od „H“ po „Z“. Každý takýto žurnál bude „registra“, ale nie „samotár“, pretože už existujú 2 žurnály.

    Trieda SingletonRegistry ( private static $instance = null; private $registry = array(); súkromná funkcia __construct()( /* ... @return Singleton */ ) // Ochrana pred vytvorením pomocou novej súkromnej funkcie Singleton __clone() ( / * ... @return Singleton */ ) // Ochrana pred vytvorením prostredníctvom klonovania súkromnej funkcie __wakeup() ( /* ... @return Singleton */ ) // Ochrana pred vytvorením prostredníctvom zrušenia serializácie verejnej statickej funkcie getInstance() ( if ( is_null(self::$instance)) ( self::$instance = new self; ) return self::$instance; ) public function set ($key, $object) ( $this->registry[$key] = $ objekt; ) verejná funkcia get($key) ( return $this->registry[$key]; ) )

    Multiton (bazén „samotárov“) alebo inak povedanéRegister Singleton (register singletonov ) - nezamieňajte s registrom Singleton (jednotný register)

    Pomerne často „registra“ slúži na uloženie „samotárov“. Ale pretože vzor „registra“ nie je „tvoriacim vzorom“, ale rád by som uvažoval o „registri“ v spojení so „samotárom“.Preto prišli so vzorom Viactónové, ktorývo svojom jadre je to „registra“ obsahujúca niekoľko „samotárov“, z ktorých každý má svoje „meno“, pod ktorým je možné sa k nemu dostať.

    Krátky: umožňuje vytvárať objekty tejto triedy, ale iba ak je objekt pomenovaný. Neexistuje žiadny príklad zo skutočného života, ale takýto príklad som vyhrabal na internete:

    Databáza tried ( private static $instances = array(); súkromná funkcia __construct() ( ) súkromná funkcia __clone() ( ) verejná statická funkcia getInstance($key) ( if(!array_key_exists($key, self::$instances)) ( self::$instance[$key] = new self(); ) return self::$instances[$key]; ) ) $master = Database::getInstance("master"); var_dump($master); // object(Database)#1 (0) ( ) $logger = Database::getInstance("logger"); var_dump($logger); // object(Database)#2 (0) ( ) $masterDupe = Database::getInstance("master"); var_dump($masterDupe); // object(Database)#1 (0) ( ) // Závažná chyba: Volanie do súkromnej databázy::__construct() z neplatného kontextu $dbFatalError = new Database(); // PHP Závažná chyba: Volanie do súkromnej databázy::__clone() $dbCloneError = klon $masterDupe;

    Objektový fond (objektový fond)

    V podstate tento vzor je "register", ktorý ukladá iba objekty, žiadne reťazce, polia atď. dátové typy.

    Továreň (továreň)

    Podstatu vzoru takmer úplne vystihuje jeho názov. Keď potrebujete dostať nejaké predmety, ako napríklad balenia džúsov, nemusíte vôbec vedieť, ako sa vyrábajú v továrni. Stačí povedať „dajte mi balíček pomarančového džúsu“ a „továreň“ vám vráti požadovaný balíček. Ako? O tom všetkom rozhoduje samotná továreň, napríklad „kopíruje“ už existujúci štandard. Hlavným účelom „fabriky“ je v prípade potreby zmeniť proces „vzhľadu“ balenia džúsu a samotnému spotrebiteľovi nebolo potrebné o tom nič hovoriť, aby si to vyžiadal ako doteraz. . Jedna továreň sa spravidla zaoberá „výrobou“ len jedného druhu „produktu“. Neodporúča sa vytvárať „továreň na šťavu“ s prihliadnutím na výrobu automobilových pneumatík. Tak ako v živote, vzor „továrne“ často vytvára „samotár“.

    Abstraktná trieda AnimalAbstract ( chránený $druh; verejná funkcia getSpecies() ( return $this->species; ) ) class Cat rozširuje AnimalAbstract ( protected $species = "mačka"; ) class Dog rozširuje AnimalAbstract ( chránený $druh = "pes"; ) trieda AnimalFactory ( verejná továreň statickej funkcie ($animal) ( switch ($animal) ( case "cat": $obj = new Cat(); break; case "pes": $obj = new Dog(); break; default : throw new Exception("Továreň na zvieratá nemohla vytvoriť zviera druhu "" . $zviera . """, 1000); ) return $obj; ) ) $cat = AnimalFactory::factory("cat"); // objekt(Cat)#1 echo $cat->getSpecies(); // mačka $pes = AnimalFactory::factory("pes"); // objekt(Pes)#1 echo $pes->getSpecies(); // pes $hippo = AnimalFactory::factory("hroch"); // Toto vyvolá výnimku

    Chcel by som poznamenať, že továrenská metóda je tiež vzor, ​​nazýva sa to továrenská metóda (továrenská metóda).

    staviteľ (staviteľ)

    Už sme teda pochopili, že „Továreň“ je automat na nápoje, už má všetko pripravené a vy len poviete, čo potrebujete. "Builder" je závod, ktorý vyrába tieto nápoje a obsahuje všetky zložité operácie a dokáže zostaviť zložité predmety z jednoduchších (obal, etiketa, voda, príchute atď.) v závislosti od požiadavky.

    Class Bottle ( public $name; public $liters; ) /** * všetci stavitelia musia */ rozhranie BottleBuilderInterface (verejná funkcia setName(); verejná funkcia setLiters(); verejná funkcia getResult(); ) class CocaColaBuilder implementuje rozhranie BottleBuilderInterface (privátne $ fľaša; verejná funkcia __construct() ( $this->bottle = new Bottle(); ) verejná funkcia setName($value) (​$this->bottle->name = $value; ) verejná funkcia setLiters($value) ($ táto->fľaša->litre = $hodnota; ) verejná funkcia getResult() ( return $this->fľaša; ) ) $juice = new CocaColaBuilder(); $juice->setName("Coca-Cola Light"); $juice->setLiters(2); $juice->getResult();

    Prototyp (prototyp)

    Pripomínajúc „továreň“ slúži aj na vytváranie predmetov, no s trochu iným prístupom. Predstavte si seba v bare, pili ste pivo a dochádza vám pivo, hovoríte barmanovi – urobte mi ešte jedno rovnaké. Barman sa na oplátku pozrie na pivo, ktoré pijete, a urobí kópiu, ako ste požadovali. PHP už má implementáciu tohto vzoru, nazýva sa .

    $newJuice = klon $šťava;

    Lenivá inicializácia (lenivá inicializácia)

    Napríklad šéf vidí zoznam výkazov pre rôzne typy činností a myslí si, že tieto výkazy už existujú, ale v skutočnosti sa zobrazujú iba názvy výkazov a samotné výkazy ešte neboli vygenerované a budú sa generovať iba podľa objednávky (napríklad kliknutím na tlačidlo Zobraziť prehľad). Špeciálnym prípadom lenivej inicializácie je vytvorenie objektu v čase, keď sa k nemu pristupuje. Na Wikipédii nájdete jeden zaujímavý, ale, pretože. podľa teórie by správnym príkladom v php bola napríklad funkcia

    Adaptér alebo Wrapper (adaptér, obal)

    Tento vzor je plne v súlade s jeho názvom. Aby „sovietska“ zástrčka fungovala cez euro zásuvku, je potrebný adaptér. Presne to robí „adaptér“ – slúži ako medziobjekt medzi dvoma ďalšími, ktoré spolu nemôžu priamo spolupracovať. Napriek definícii v praxi stále vidím rozdiel medzi Adaptérom a Wrapperom.

    Trieda MyClass ( public function methodA() () ) class MyClassWrapper ( public function __construct()( $this->myClass = new MyClass(); ) verejná funkcia __call($name, $arguments)( Log::info("You sa chystajú zavolať metódu $name."); return call_user_func_array(pole($this->myClass, $name), $arguments); ) ) $obj = new MyClassWrapper(); $obj->methodA();

    Injekcia závislosti (injekcia závislosti)

    Injekcia závislostí vám umožňuje presunúť časť zodpovednosti za niektoré funkcie na iné objekty. Napríklad, ak potrebujeme prijať nových zamestnancov, nemôžeme si vytvoriť vlastné personálne oddelenie, ale zaviesť závislosť na personálnej spoločnosti, ktorá zase na našu prvú žiadosť „potrebujeme človeka“ bude fungovať buď ako samotné personálne oddelenie, alebo nájsť inú spoločnosť (pomocou „lokátora služieb“), ktorá tieto služby poskytne.
    „Dependency Injection“ umožňuje posúvať a zamieňať jednotlivé časti spoločnosti bez straty celkovej funkčnosti.

    Trieda AppleJuice () // táto metóda je primitívnou implementáciou vzoru injekcie Dependency a ďalej uvidíte túto funkciu getBottleJuice()( $obj = new Jablkový džús Jablkový džús)( return $obj; ) ) $bottleJuice = getBottleJuice();

    Teraz si predstavte, že už nechceme jablkový džús, ale pomaranč.

    Trieda AppleJuice() Trieda Pomarančový džús() // táto metóda implementuje funkciu vstrekovania závislosti getBottleJuice()( $obj = new Pomarančový džús; // skontrolujte objekt, inak nám zrazu podsunuli pivo (pivo nie je džús) if($obj instanceof Pomarančový džús)( return $obj; ) )

    Ako vidíte, museli sme zmeniť nielen druh šťavy, ale aj kontrolu na typ šťavy, čo nie je príliš pohodlné. Oveľa správnejšie je použiť princíp inverzie závislosti:

    Rozhranie Juice () Trieda AppleJuice implementuje Juice () Trieda OrangeJuice implementuje Juice () funkcia getBottleJuice()( $obj = new OrangeJuice; // skontrolujte objekt, inak nám zrazu podstrčili pivo (pivo nie je džús) if($obj instanceof Šťava)( return $obj; ) )

    Inverzia závislosti sa niekedy zamieňa s injekciou závislosti, ale nemali by sa zamieňať, pretože. Inverzia závislosti je princíp, nie vzor.

    Service Locator (lokátor služieb)

    "Service Locator" je metóda implementácie "Dependency Injection". Vracia rôzne typy objektov v závislosti od inicializačného kódu. Nech je úlohou doručiť náš balík šťavy, vytvorený staviteľom, továrňou alebo čímkoľvek iným, kamkoľvek si kupujúci želá. Lokátorovi povieme „dajte nám doručovaciu službu“ a požiadame službu, aby doručila džús na správnu adresu. Dnes jedna služba a zajtra môže byť ďalšia. Je nám jedno, o akú konkrétnu službu ide, je pre nás dôležité vedieť, že táto služba doručí to, čo jej povieme a kde to povieme. Služby zas implementujú Deliver<предмет>na<адрес>».

    Ak hovoríme o skutočnom živote, potom pravdepodobne dobrým príkladom lokátora služieb môže byť rozšírenie PDO php, pretože. dnes pracujeme s databázou MySQL a zajtra môžeme pracovať s PostgreSQL. Ako ste už pochopili, pre našu triedu nie je dôležité, do ktorej databázy posiela svoje údaje, dôležité je, že to dokáže.

    $db = nový PDO(" mysql:dbname=test;host=localhost", $user, $pass); $db = nový PDO(" pgsql:dbname=test host=localhost", $user, $pass);

    Rozdiel medzi injekciou závislosti a lokalizátorom služieb

    Ak ste si to ešte nevšimli, rád by som to vysvetlil. Injekcia závislosti v dôsledku toho nevracia službu (ktorá môže niekde niečo dodať), ale objekt, ktorého dáta používa.