Registry, Factory Method, Service Locator və Dependency Injection nümunələrindən istifadə edərək həll yolu tapmaq. PHP-də OOP proqramlarında obyektin işə salınması problemi

PHP-də Registry modelinin tətbiqi haqqında sizə məlumat verməyə çalışacağam. Qeydiyyat məlumatları saxlamaq və sistem modulları arasında ötürmək üçün nəzərdə tutulmuş qlobal dəyişənlər üçün OOP əvəzedicisidir. Müvafiq olaraq, o, standart xüsusiyyətlərə malikdir - yazmaq, oxumaq, silmək. Budur tipik bir tətbiq.

Beləliklə, $key = $value - Registry::set($key, $value) $key - Registry::get($key) unset($key) - Registry::Remove metodlarının axmaq şəkildə dəyişdirilməsini əldə edirik. ($key ) Sadəcə aydın deyil - niyə bu əlavə kod. Beləliklə, gəlin sinifimizə qlobal dəyişənlərin edə bilmədiyi şeyi etməyi öyrədək. Üzərinə bibər əlavə edək.

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

Nümunənin tipik tapşırıqlarına mən dəyişəni dəyişikliklərdən bloklamaq qabiliyyətini əlavə etdim, bu, böyük layihələrdə çox rahatdır, təsadüfən heç bir şey daxil etməyəcəksiniz. Məsələn, verilənlər bazası ilə işləmək üçün əlverişlidir
müəyyən edin('DB_DNS', 'mysql:host=localhost;dbname= ’);
müəyyən edin('DB_USER', ' ’);
müəyyən edin('DB_PASSWORD', ' ’);
müəyyən ('DB_HANDLE');

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

İndi kodun izahı üçün məlumatları saxlamaq üçün biz $data statik dəyişənindən istifadə edirik, $lock dəyişəni dəyişiklik üçün kilidlənmiş açarlar haqqında məlumatları saxlayır. Şəbəkədə dəyişənin kilidli olub-olmadığını yoxlayırıq və onu dəyişdiririk və ya reyestrə əlavə edirik. Silindikdə, biz də kilidi yoxlayırıq; standart isteğe bağlı parametr istisna olmaqla, alıcı dəyişməz qalır. Yaxşı, nədənsə nadir hallarda istifadə olunan istisnalarla işləməyə diqqət yetirməyə dəyər.Yeri gəlmişkən, artıq istisnalarla bağlı bir layihəm var, məqaləni gözləyin. Aşağıda test üçün kod layihəsi var, burada test haqqında məqalə var, TDD-nin pərəstişkarı olmasam da, onu yazmaq da zərər verməz.

Növbəti məqalədə məlumatların işə salınmasını əlavə etməklə və “tənbəllik” tətbiq etməklə funksionallığı daha da genişləndirəcəyik.

Bu nümunə, Singleton kimi, nadir hallarda tərtibatçıların müsbət reaksiyasına səbəb olur, çünki tətbiqləri sınaqdan keçirərkən eyni problemlərə səbəb olur. Buna baxmayaraq, danlayırlar, lakin fəal şəkildə istifadə edirlər. Singleton kimi, Reyestr nümunəsi də bir çox tətbiqlərdə olur və bu və ya digər şəkildə müəyyən problemlərin həllini xeyli asanlaşdırır.

Hər iki variantı ardıcıllıqla nəzərdən keçirək.

"Saf registr" və ya sadəcə Registry adlanan şey statik interfeysli bir sinfin həyata keçirilməsidir. Singleton modelindən əsas fərq ondan ibarətdir ki, o, ən azı bir sinif nümunəsi yaratmaq qabiliyyətini bloklayır. Bunu nəzərə alaraq, __clone() və __wakeup() sehrli üsullarını özəl və ya qorunan modifikatorun arxasında gizlətməyin mənası yoxdur.

Qeydiyyat sinfi iki statik metoda malik olmalıdır - alıcı və təyinedici. Ayarlayıcı ötürülən obyekti verilmiş açara bağlama ilə yaddaşa yerləşdirir. Alıcı, müvafiq olaraq, mağazadan bir obyekt qaytarır. Mağaza assosiativ açar-dəyər massivindən başqa bir şey deyil.

Qeyd dəftərinə tam nəzarət etmək üçün başqa bir interfeys elementi təqdim olunur - bir obyekti yaddaşdan silməyə imkan verən bir üsul.

Singleton nümunəsi ilə eyni problemlərə əlavə olaraq, daha ikisi var:

  • başqa bir asılılıq növünün tətbiqi - qeyd açarlarından;
  • iki fərqli qeyd açarının eyni obyektə istinadı ola bilər

Birinci halda əlavə asılılıqdan qaçmaq mümkün deyil. Müəyyən dərəcədə biz əsas adlara bağlanırıq.

İkinci problem Registry::set() metoduna çek daxil etməklə həll edilir:

İctimai statik funksiya dəsti($açar, $element) ( əgər (!array_key_mövcuddursa($açar, self::$_registr)) ( foreach (self::$_registr $val olaraq) ( əgər ($val === $element) ( yeni İstisna atın("Element artıq mövcuddur"); ) ) self::$_registr[$key] = $element; ) )

« Təmiz Registry nümunəsi"başqa bir problemə səbəb olur - sinif adı vasitəsilə təyinedici və alıcıya daxil olmaq ehtiyacı səbəbindən asılılığın artması. Bu yanaşma mövcud olduqda Singleton modelində olduğu kimi obyektə istinad yarada və onunla işləyə bilməzsiniz:

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

Burada bizim Singleton instansiyasına istinadı, məsələn, cari sinifin mülkiyyətində saxlamaq və OOP ideologiyasının tələb etdiyi kimi onunla işləmək imkanımız var: onu birləşdirilmiş obyektlərə parametr kimi ötürmək və ya nəsillərdə istifadə etmək.

Bu problemi həll etmək üçün var Singleton Registry tətbiqi, bir çox insanın xoşuna gəlmir, çünki bu, lazımsız kod kimi görünür. Düşünürəm ki, bu münasibətin səbəbi OOP prinsiplərinin müəyyən qədər anlaşılmazlığı və ya onlara qəsdən etinasızlıqdır.

_registr[$key] = $obyekt; ) statik ictimai funksiya get($key) ( qaytar self::getInstance()->_registr[$key]; ) özəl funksiya __wakeup() ( ) özəl funksiya __construct() ( ) özəl funksiya __clone() ( ) ) ?>

Pula qənaət etmək üçün üsullar və xüsusiyyətlər üçün şərh bloklarını qəsdən buraxdım. Məncə, bunlar lazım deyil.

Artıq dediyim kimi, əsas fərq ondan ibarətdir ki, indi reyestr həcminə istinad saxlamaq və hər dəfə statik metodlara çətin zənglərdən istifadə etməmək mümkündür. Bu variant mənə bir qədər düzgün görünür. Mənim fikrimlə razılaşmaq və ya razılaşmamaq mənim fikrimin özü kimi o qədər də önəmli deyil. Heç bir icra incəlikləri nümunəni qeyd olunan bir sıra çatışmazlıqlardan aradan qaldıra bilməz.

PHP-də OOP proqramlarında obyektin işə salınması problemi. Registry, Factory Method, Service Locator və Dependency Injection nümunələrindən istifadə edərək həll yolu tapmaq

Elə olur ki, proqramçılar uğurlu həlləri dizayn nümunələri şəklində birləşdirir. Naxışlar haqqında çoxlu ədəbiyyat var. The Gang of Four-un Erich Gamma, Richard Helm, Ralph Johnson və John Vlissides tərəfindən "Dizayn Nümunələri" kitabı və bəlkə də Martin Faulerin "Müəssisə Tətbiqi Arxitekturasının Nümunələri" şübhəsiz ki, klassiklər hesab olunur. Nümunələrlə oxuduğum ən yaxşı şey. PHP-də - bu. Elə olur ki, bütün bu ədəbiyyat OOP-u yenicə mənimsəməyə başlayan insanlar üçün kifayət qədər mürəkkəbdir. Ona görə də məndə ən faydalı hesab etdiyim bəzi nümunələri olduqca sadələşdirilmiş formada təqdim etmək fikri yarandı. sözlə, bu məqalə mənim ilk KISS üslubunda dizayn nümunələrini şərh etmək cəhdimdir.
Bu gün biz OOP tətbiqində obyektin işə salınması ilə bağlı hansı problemlərin yarana biləcəyi və bu problemləri həll etmək üçün bəzi məşhur dizayn nümunələrindən necə istifadə edə biləcəyiniz haqqında danışacağıq.

Misal

Müasir OOP tətbiqi onlarla, yüzlərlə və bəzən minlərlə obyektlə işləyir. Gəlin bu obyektlərin tətbiqlərimizdə necə işə salındığına daha yaxından nəzər salaq. Obyektin işə salınması bu məqalədə bizi maraqlandıran yeganə aspektdir, ona görə də bütün “əlavə” tətbiqləri buraxmaq qərarına gəldim.
Deyək ki, biz xüsusi URI-yə GET sorğusu göndərə və server cavabından HTML qaytara bilən super-duper faydalı sinif yaratdıq. Sinifimizin çox sadə görünməməsi üçün o da nəticəni yoxlasın və server “səhv” cavab verərsə, istisna atın.

Class Grabber ( ictimai funksiya get($url) (/** HTML kodunu qaytarır və ya istisna atır */) )

Gəlin başqa bir sinif yaradaq, onun obyektləri qəbul edilmiş HTML-nin süzülməsinə cavabdeh olacaq. Filtr metodu HTML kodunu və CSS seçicisini arqument kimi götürür və verilmiş seçici üçün tapılan elementlər massivini qaytarır.

Class HtmlExtractor ( ictimai funksiya filter($html, $selector) (/** filtrlənmiş elementlər massivini qaytarır */) )

İndi təsəvvür edin ki, verilmiş açar sözlər üçün Google-da axtarış nəticələri əldə etməliyik. Bunun üçün sorğu göndərmək üçün Grabber sinfindən və lazımi məzmunu çıxarmaq üçün HtmlExtractor sinfindən istifadə edəcək başqa bir sinif təqdim edəcəyik. O, həmçinin URI-nin qurulması üçün məntiqi, qəbul edilmiş HTML-ni süzgəcdən keçirmək üçün seçicini və əldə edilmiş nəticələrin işlənməsini ehtiva edəcəkdir.

Class GoogleFinder ( şəxsi $grabber; özəl $filter; ictimai funksiya __construct() ( $this->grabber = new Grabber(); $this->filter = new HtmlExtractor(); ) ictimai funksiya find($searchString) ( /* * əsaslanmış nəticələr massivini qaytarır */) )

Grabber və HtmlExtractor obyektlərinin işə salınmasının GoogleFinder sinifinin konstruktorunda olduğunu gördünüzmü? Gəlin bu qərarın nə qədər yaxşı olduğunu düşünək.
Əlbəttə ki, konstruktorda obyektlərin yaradılmasının sərt kodlaşdırılması yaxşı fikir deyil. Və buna görə. Birincisi, real sorğu göndərməmək üçün test mühitində Grabber sinfini asanlıqla ləğv edə bilməyəcəyik. Ədalətli olmaq üçün bunun Reflection API istifadə edərək edilə biləcəyini söyləməyə dəyər. Bunlar. texniki imkan mövcuddur, lakin bu, ən rahat və aşkar yoldan uzaqdır.
İkincisi, GoogleFinder məntiqini digər Grabber və HtmlExtractor tətbiqləri ilə yenidən istifadə etmək istəsək, eyni problem yaranacaq. Asılılıqların yaradılması sinif konstruktorunda kodlaşdırılır. Və ən yaxşı halda, biz GoogleFinder-i miras ala və onun konstruktorunu ləğv edə biləcəyik. Və hətta bundan sonra, yalnız grabber və filtr xüsusiyyətlərinin əhatə dairəsi qorunursa və ya ictimaidir.
Son bir məqam, biz hər dəfə yeni GoogleFinder obyekti yaratdıqda yaddaşda yeni bir cüt asılılıq obyekti yaradılacaq, baxmayaraq ki, bir neçə GoogleFinder obyektində bir Grabber obyekti və bir HtmlExtractor obyektindən çox asanlıqla istifadə edə bilərik.
Düşünürəm ki, siz artıq başa düşürsünüz ki, asılılıq başlanğıcının sinifdən kənara köçürülməsi lazımdır. Artıq hazırlanmış asılılıqların GoogleFinder sinifinin konstruktoruna ötürülməsini tələb edə bilərik.

Class GoogleFinder ( şəxsi $grabber; özəl $filter; ictimai funksiya __construct(Grabber $grabber, HtmlExtractor $filter) ( $this->grabber = $grabber; $this->filter = $filter; ) ictimai funksiya find($searchString) ( /** əsaslanmış nəticələr massivini qaytarır */))

Digər tərtibatçılara öz Grabber və HtmlExtractor tətbiqlərini əlavə etmək və istifadə etmək imkanı vermək istəyiriksə, onda onlar üçün interfeyslər təqdim etməyi düşünməliyik. Bu vəziyyətdə bu, yalnız faydalı deyil, həm də zəruridir. Hesab edirəm ki, əgər biz layihədə yalnız bir tətbiqdən istifadə ediriksə və gələcəkdə yenisini yaratmağı gözləmiriksə, o zaman interfeys yaratmaqdan imtina etməliyik. Vəziyyətə uyğun hərəkət etmək və buna real ehtiyac olduqda sadə refaktorinq etmək daha yaxşıdır.
İndi bizdə bütün lazımi siniflər var və biz nəzarətçidə GoogleFinder sinfindən istifadə edə bilərik.

Sinif Nəzarətçisi ( ictimai funksiya fəaliyyət() ( /* Bəzi şeylər */ $finder = yeni GoogleFinder(yeni Grabber(), yeni HtmlExtractor()); $nəticələr = $finder->

Aralıq nəticələri ümumiləşdirək. Çox az kod yazdıq və ilk baxışdan səhv bir şey etmədik. Bəs... GoogleFinder kimi obyekti başqa yerdə istifadə etməliyiksə? Biz onun yaradılmasını təkrarlamalı olacağıq. Bizim nümunəmizdə bu sadəcə bir xəttdir və problem o qədər də nəzərə çarpmır. Praktikada obyektlərin işə salınması kifayət qədər mürəkkəb ola bilər və 10 sətir və ya daha çox çəkə bilər. Kodun təkrarlanmasına xas olan digər problemlər də yaranır. Əgər refaktorinq prosesi zamanı istifadə olunan sinfin adını və ya obyektin başlanğıc məntiqini dəyişmək lazımdırsa, bütün yerləri əl ilə dəyişməli olacaqsınız. Məncə bunun necə baş verdiyini bilirsiniz :)
Bir qayda olaraq, sərt kod sadə şəkildə həll edilir. Dublikat dəyərlər adətən konfiqurasiyaya daxil edilir. Bu, istifadə olunduğu bütün yerlərdə dəyərləri mərkəzi olaraq dəyişdirməyə imkan verir.

Reyestr şablonu.

Beləliklə, biz obyektlərin yaradılmasını konfiqurasiyaya köçürmək qərarına gəldik. Gəl bunu edək.

$registr = yeni ArrayObject(); $registry["grabber"] = new Grabber(); $registry["filter"] = new HtmlExtractor(); $registry["google_finder"] = yeni GoogleFinder($registry["grabber"], $registry["filtr"]);
Etməli olduğumuz şey ArrayObject-imizi nəzarətçiyə ötürməkdir və problem həll olunur.

Sinif Nəzarətçisi ( şəxsi $registry; ictimai funksiya __construct(ArrayObject $registry) ( $this->registr = $registry; ) ictimai funksiya action() ( /* Bəzi şeylər */ $results = $this->registr["google_finder" ]->find("axtarış sətri"); /* Nəticələrlə bir şey edin */ ) )

Biz Reyestr ideyasını daha da inkişaf etdirə bilərik. ArrayObject-i miras alın, yeni sinif daxilində obyektlərin yaradılmasını əhatə edin, işə salındıqdan sonra yeni obyektlərin əlavə edilməsini qadağan edin və s. Ancaq mənim fikrimcə, verilən kod Registry şablonunun nə olduğunu tam aydınlaşdırır. Bu nümunə generativ xarakter daşımır, lakin problemlərimizi həll etmək üçün müəyyən bir şəkildə gedir. Reyestr sadəcə olaraq obyektləri saxlaya biləcəyimiz və tətbiq daxilində onları ötürə biləcəyimiz bir konteynerdir. Obyektlərin əlçatan olması üçün əvvəlcə onları yaratmalı və bu konteynerdə qeydiyyatdan keçirməliyik. Bu yanaşmanın üstünlüklərini və mənfi cəhətlərini nəzərdən keçirək.
İlk baxışdan məqsədimizə nail olduq. Biz sinif adlarının sərt kodlaşdırılmasını dayandırdıq və bir yerdə obyektlər yaratdıq. Biz obyektləri bir nüsxədə yaradırıq ki, bu da onların təkrar istifadəsinə zəmanət verir. Obyektlərin yaradılması məntiqi dəyişirsə, onda tətbiqdə yalnız bir yeri redaktə etmək lazımdır. Bonus olaraq biz Reyestrdəki obyektləri mərkəzləşdirilmiş şəkildə idarə etmək imkanı əldə etdik. Biz asanlıqla bütün mövcud obyektlərin siyahısını əldə edə və onlarla bəzi manipulyasiyalar həyata keçirə bilərik. İndi bu şablonda nəyi bəyənmədiyimizə baxaq.
Birincisi, obyekti Reyestrdə qeydiyyatdan keçirməzdən əvvəl yaratmalıyıq. Müvafiq olaraq, "lazımsız obyektlərin" yaradılması ehtimalı yüksəkdir, yəni. yaddaşda yaradılacaq, lakin tətbiqdə istifadə edilməyəcəklər. Bəli, biz obyektləri reyestrə dinamik şəkildə əlavə edə bilərik, yəni. yalnız xüsusi sorğunun işlənməsi üçün lazım olan obyektləri yaradın. Bu və ya digər şəkildə biz bunu əl ilə idarə etməli olacağıq. Müvafiq olaraq, zaman keçdikcə onu saxlamaq çox çətin olacaq.
İkincisi, bizdə yeni nəzarətçi asılılığı var. Bəli, biz Reyestrdə statik metod vasitəsilə obyektləri qəbul edə bilərik ki, Registri konstruktora ötürməyək. Amma məncə, bunu etməməlisən. Statik metodlar obyekt daxilində asılılıqlar yaratmaqdan və sınaqda çətinliklərdən (bu mövzuda) daha sıx əlaqədir.
Üçüncüsü, nəzarətçi interfeysi bizə hansı obyektlərdən istifadə etdiyi barədə heç nə demir. Reyestrdə mövcud olan istənilən obyekti nəzarətçidə əldə edə bilərik. Bütün mənbə kodunu yoxlayana qədər nəzarətçinin hansı obyektlərdən istifadə etdiyini söyləmək bizim üçün çətin olacaq.

Zavod üsulu

Qeyd dəftəri ilə bağlı ən böyük narahatlığımız ondan ibarətdir ki, obyektə daxil olmaq üçün əvvəlcə işə salınmalıdır. Konfiqurasiyada bir obyekti işə salmaq əvəzinə, biz obyektlərin yaradılması məntiqini başqa bir sinifə ayıra bilərik, bu da bizə lazım olan obyekti qurmaq üçün “istəmək” olar. Obyektlərin yaradılmasına cavabdeh olan siniflərə fabriklər deyilir. Və dizayn nümunəsi Zavod metodu adlanır. Nümunə olaraq bir fabrikə baxaq.

Class Factory ( getGoogleFinder() ictimai funksiyası (yeni GoogleFinder($this->getGrabber(), $this->getHtmlExtractor()); ) özəl funksiya getGrabber() (yeni Grabber(); ) özəl funksiya getHtmlExtractor() ( yeni HtmlFiletr qaytarın(); ) )

Bir qayda olaraq, bir növ obyektin yaradılmasına cavabdeh olan fabriklər hazırlanır. Bəzən bir fabrik əlaqəli obyektlər qrupu yarada bilər. Obyektlərin yenidən yaradılmasının qarşısını almaq üçün bir xüsusiyyətə keşləşdirmədən istifadə edə bilərik.

Class Factory ( şəxsi $finder; ictimai funksiya getGoogleFinder() ( if (null === $this->finder) ( $this->finder = new GoogleFinder($this->getGrabber(), $this->getHtmlExtractor() ); ) $this->finderi qaytarın; ) )

Biz zavod metodunu parametrləşdirə və daxil olan parametrdən asılı olaraq işə salmağı digər fabriklərə həvalə edə bilərik. Bu artıq Abstrakt Fabrika şablonu olacaq.
Tətbiqi modullaşdırmaq lazımdırsa, hər moduldan öz zavodlarını təmin etməyi tələb edə bilərik. Fabriklərin mövzusunu daha da inkişaf etdirə bilərik, amma məncə bu şablonun mahiyyəti aydındır. Nəzarətçidə fabrikdən necə istifadə edəcəyimizi görək.

Class Controller ( şəxsi $factory; ictimai funksiya __construct(Factory $factory) ( $this->factory = $factory; ) public function action() ( /* Bəzi şeylər */ $results = $this->factory->getGoogleFinder( )->find("axtarış sətri"); /* Nəticələrlə bir şey edin */ ) )

Bu yanaşmanın üstünlükləri onun sadəliyini ehtiva edir. Bizim obyektlərimiz açıq şəkildə yaradılmışdır və IDE-niz sizi asanlıqla bunun baş verdiyi yerə aparacaq. Biz Reyestr problemini də həll etdik ki, yaddaşdakı obyektlər yalnız fabrikdən bunu "xahiş etdikdə" yaradılsın. Amma biz hələ ki, nəzarətçilərə lazımi zavodları necə təmin edəcəyimizə qərar verməmişik. Burada bir neçə variant var. Statik üsullardan istifadə edə bilərsiniz. Nəzarətçilərə lazımi fabrikləri özləri yaratmağa icazə verə bilərik və kopyala-yapışdırmaqdan xilas olmaq üçün bütün cəhdlərimizi ləğv edə bilərik. Siz fabriklər fabriki yarada və yalnız bunu nəzarətçiyə ötürə bilərsiniz. Lakin nəzarətçidə obyektlərin əldə edilməsi bir az daha mürəkkəbləşəcək və fabriklər arasında asılılıqları idarə etməli olacaqsınız. Bundan əlavə, tətbiqimizdə modullardan istifadə etmək istəsək nə edəcəyimiz, modul fabriklərinin necə qeydiyyata alınacağı, müxtəlif modullardan fabriklər arasında əlaqələri necə idarə edəcəyimiz tam aydın deyil. Ümumiyyətlə, biz fabrikin əsas üstünlüyünü - obyektlərin açıq şəkildə yaradılmasını itirmişik. Və biz hələ də "qeyri-müəyyən" nəzarətçi interfeysi problemini həll etməmişik.

Xidmət Lokator

Service Locator şablonu fabriklərin parçalanmasının çatışmazlığını həll etməyə və obyektlərin yaradılmasını avtomatik və mərkəzləşdirilmiş şəkildə idarə etməyə imkan verir. Bu barədə düşünsək, tətbiqimizdə obyektlərin yaradılmasına və bu obyektlər arasında əlaqələrin idarə olunmasına cavabdeh olacaq əlavə abstraksiya qatını təqdim edə bilərik. Bu təbəqənin bizim üçün obyektlər yarada bilməsi üçün biz ona bunu necə edəcəyimiz barədə məlumat verməliyik.
Xidmət Lokator nümunəsi şərtləri:
  • Xidmət konteynerdən əldə edilə bilən hazır obyektdir.
  • Service Definition – xidmətin işə salınması məntiqi.
  • Konteyner (Xidmət Konteyneri) bütün təsvirləri saxlayan və onların əsasında xidmətlər yarada bilən mərkəzi obyektdir.
İstənilən modul öz xidmət təsvirlərini qeyd edə bilər. Konteynerdən bəzi xidmətlər əldə etmək üçün biz bunu açarla tələb etməli olacağıq. Service Locator-u tətbiq etmək üçün bir çox variant var; ən sadə versiyada biz ArrayObject-dən konteyner kimi, bağlanma isə xidmətlərin təsviri kimi istifadə edə bilərik.

Class ServiceContainer ArrayObject-i genişləndirir ( ictimai funksiya get($key) ( if (is_callable($this[$key))) ( return call_user_func($this[$key]); ) throw new \RuntimeException("Xidmət tərifini tapmaq mümkün deyil" açar [ $key ]"); ) )

Sonra Təriflərin qeydiyyatı belə görünəcək:

$container = new ServiceContainer(); $container["grabber"] = function () ( new Grabber(); ); $container["html_filter"] = function () (yeni HtmlExtractor qaytarın(); ); $container["google_finder"] = function() use ($container) (yeni GoogleFinder qaytarın($container->get("grabber"), $container->get("html_filter")); );

Və nəzarətçidə istifadə belədir:

Class Controller ( şəxsi $container; ictimai funksiya __construct(ServiceContainer $container) ( $this->container = $container; ) public function action() ( /* Bəzi şeylər */ $nəticələr = $this->container->get( "google_finder")->find("axtarış sətri"); /* Nəticələrlə bir şey edin */ ) )

Xidmət Konteyneri çox sadə və ya çox mürəkkəb ola bilər. Məsələn, Symfony Service Container bir çox funksiyaları təqdim edir: parametrlər, xidmətlərin əhatə dairəsi, teqlər, ləqəblər, özəl xidmətlər üzrə xidmətlərin axtarışı, bütün xidmətləri (kompilyator keçidləri) əlavə etdikdən sonra konteynerə dəyişiklik etmək imkanı və s. DIExtraBundle standart tətbiqin imkanlarını daha da genişləndirir.
Ancaq nümunəmizə qayıdaq. Gördüyünüz kimi, Service Locator yalnız əvvəlki şablonlarla eyni problemləri həll etmir, həm də öz xidmət tərifləri ilə modulların istifadəsini asanlaşdırır.
Bundan əlavə, çərçivə səviyyəsində biz əlavə abstraksiya səviyyəsini aldıq. Məhz, ServiceContainer::get metodunu dəyişdirməklə, məsələn, obyekti proxy ilə əvəz edə bilərik. Proksi obyektlərin tətbiqi sahəsi yalnız tərtibatçının təxəyyülü ilə məhdudlaşır. Burada siz AOP paradiqmasını həyata keçirə bilərsiniz, LazyLoading və s.
Lakin əksər tərtibatçılar hələ də Service Locator-u anti-naxış hesab edirlər. Çünki nəzəri olaraq bizdə o qədər sözdə ola bilər Container Aware sinifləri (yəni konteynerə istinad olan siniflər). Məsələn, daxilində istənilən xidməti ala biləcəyimiz Controllerimiz.
Bunun niyə pis olduğunu görək.
Birincisi, yenidən sınaqdan keçirin. Yalnız testlərdə istifadə olunan siniflər üçün istehza yaratmaq əvəzinə, bütün konteyneri ələ salmalı və ya real konteynerdən istifadə etməli olacaqsınız. Birinci variant sizə uyğun deyil, çünki... testlərdə çoxlu lazımsız kod yazmalısan, ikincisi, çünki o, vahid sınaq prinsiplərinə ziddir və sınaqların saxlanması üçün əlavə xərclərə səbəb ola bilər.
İkincisi, refaktor etmək bizim üçün çətin olacaq. Konteynerdəki hər hansı bir xidməti (və ya ServiceDefinition) dəyişdirməklə, biz bütün asılı xidmətləri yoxlamaq məcburiyyətində qalacağıq. Və bu problemi IDE-nin köməyi ilə həll etmək mümkün deyil. Tətbiq boyu belə yerləri tapmaq o qədər də asan olmayacaq. Asılı xidmətlərə əlavə olaraq, konteynerdən refaktorlaşdırılmış xidmətin alındığı bütün yerləri də yoxlamalı olacaqsınız.
Üçüncü səbəb, xidmətlərin konteynerdən nəzarətsiz çəkilməsi gec-tez kodda qarışıqlığa və lazımsız qarışıqlığa səbəb olacaqdır. Bunu izah etmək çətindir, sadəcə olaraq bu və ya digər xidmətin necə işlədiyini başa düşmək üçün getdikcə daha çox vaxt sərf etməli olacaqsınız, başqa sözlə, onun nə etdiyini və ya sinfin necə işlədiyini yalnız onun bütün mənbə kodunu oxumaqla tam başa düşə bilərsiniz.

Asılılıq Enjeksiyonu

Tətbiqdə konteynerin istifadəsini məhdudlaşdırmaq üçün başqa nə edə bilərsiniz? Siz bütün istifadəçi obyektlərinin, o cümlədən kontrollerlərin yaradılmasına nəzarəti çərçivəyə ötürə bilərsiniz. Başqa sözlə, istifadəçi kodu konteynerin get metodunu çağırmamalıdır. Nümunəmizdə konteynerə nəzarətçi üçün Tərif əlavə edə bilərik:

$container["google_finder"] = function() istifadə ($container) (yeni Nəzarətçi qaytarın(Grabber $grabber); );

Və nəzarətçidəki konteynerdən xilas olun:

Class Controller ( şəxsi $finder; ictimai funksiya __construct(GoogleFinder $finder) ( $this->finder = $finder; ) public function action() ( /* Bəzi şeylər */ $nəticələr = $this->finder->find( "search string"); /* Nəticələrlə bir şey edin */ ) )

Bu yanaşma (Xidmət Konteynerinə giriş müştəri siniflərinə təqdim edilmədikdə) Dependency Injection adlanır. Ancaq bu şablonun həm üstünlükləri, həm də mənfi cəhətləri var. Nə qədər ki, biz vahid məsuliyyət prinsipinə əməl edirik, kod çox gözəl görünür. İlk növbədə, biz müştəri siniflərində konteynerdən xilas olduq, onların kodunu daha aydın və sadə etdik. Lazımi asılılıqları əvəz etməklə nəzarətçini asanlıqla sınaqdan keçirə bilərik. Biz TDD və ya BDD yanaşmasından istifadə edərək hər bir sinfi digərlərindən (o cümlədən nəzarətçi sinifləri) müstəqil olaraq yarada və sınaqdan keçirə bilərik. Testlər yaratarkən, biz konteynerdən mücərrəd çıxara bilərik və daha sonra xüsusi nümunələrdən istifadə etmək lazım olduqda Tərif əlavə edə bilərik. Bütün bunlar kodumuzu daha sadə və aydın, testi isə daha şəffaf edəcək.
Amma sikkənin digər tərəfini də qeyd etmək lazımdır. Fakt budur ki, nəzarətçilər çox xüsusi siniflərdir. Nəzarətçinin, bir qayda olaraq, bir sıra hərəkətlər ehtiva etməsi ilə başlayaq, bu, vahid məsuliyyət prinsipini pozduğunu bildirir. Nəticə etibarilə, nəzarətçi sinfi müəyyən bir hərəkəti yerinə yetirmək üçün lazım olandan daha çox asılılığa malik ola bilər. Tənbəl başlanğıcdan istifadə (obyekt ilk istifadə zamanı yaradılır və ondan əvvəl yüngül proksi istifadə olunur) performans problemini müəyyən dərəcədə həll edir. Amma memarlıq nöqteyi-nəzərindən nəzarətçidən çoxlu asılılıqlar yaratmaq da tamamilə düzgün deyil. Bundan əlavə, nəzarətçilərin sınaqdan keçirilməsi adətən lazımsız bir əməliyyatdır. Hər şey, əlbəttə ki, ərizənizdə testin necə təşkil olunduğundan və özünüzə bu barədə necə hiss etdiyinizdən asılıdır.
Əvvəlki paraqrafdan siz başa düşdünüz ki, Dependency Injection-dan istifadə memarlıq problemlərini tamamilə aradan qaldırmır. Buna görə də, konteynerə keçidi nəzarətçilərdə saxlayıb-saxlamamağınızın sizin üçün necə daha əlverişli olacağını düşünün. Burada heç bir düzgün həll yolu yoxdur. Hesab edirəm ki, nəzarətçi kodu sadə qaldığı müddətcə hər iki yanaşma yaxşıdır. Ancaq, şübhəsiz ki, nəzarətçilərə əlavə olaraq Conatiner Aware xidmətləri yaratmamalısınız.

nəticələr

Yaxşı, deyilənlərin hamısını ümumiləşdirməyin vaxtı gəldi. Və çox şey deyilib... :)
Beləliklə, obyektlərin yaradılması işini strukturlaşdırmaq üçün aşağıdakı nümunələrdən istifadə edə bilərik:
  • Qeydiyyat: Şablonun aşkar çatışmazlıqları var, onlardan ən əsası obyektləri ümumi konteynerə qoymadan əvvəl yaratmaq ehtiyacıdır. Aydındır ki, biz ondan istifadənin faydasından daha çox problem görəcəyik. Bu, şablonun ən yaxşı istifadəsi deyil.
  • Zavod üsulu: Nümunənin əsas üstünlüyü: obyektlər açıq şəkildə yaradılır. Əsas çatışmazlıq: nəzarətçilər ya fabrikləri özləri yaratmaqdan narahat olmalıdırlar, bu da sinif adlarının kodlaşdırılması problemini tamamilə həll etmir, ya da çərçivə nəzarətçiləri bütün lazımi zavodlarla təmin etmək üçün məsuliyyət daşımalıdır, bu o qədər də açıq olmayacaq. Obyektlərin yaradılması prosesini mərkəzdən idarə etmək imkanı yoxdur.
  • Xidmət Lokator: Obyektlərin yaradılmasına nəzarət etmək üçün daha təkmil üsul. Obyektlərin yaradılması zamanı rast gəlinən ümumi tapşırıqları avtomatlaşdırmaq üçün əlavə abstraksiya səviyyəsindən istifadə edilə bilər. Misal üçün:
    class ServiceContainer ArrayObject-i genişləndirir ( ictimai funksiya get($key) ( if (is_callable($this[$key))) ( $obj = call_user_func($this[$key]); if ($obj instance of RequestAwareInterface) ( $obj- >setRequest($this->get("request")); ) return $obj; ) atmaq yeni \RuntimeException("[ $key ] düyməsi altında xidmət tərifini tapmaq mümkün deyil"); ))
    Service Locator-un dezavantajı ondan ibarətdir ki, siniflərin ictimai API-si informativ olmağı dayandırır. Hansı xidmətlərdən istifadə olunduğunu anlamaq üçün sinfin bütün kodunu oxumaq lazımdır. Konteynerə istinad olan sinfi yoxlamaq daha çətindir.
  • Asılılıq Enjeksiyonu: Əslində biz əvvəlki nümunə ilə eyni Xidmət Konteynerindən istifadə edə bilərik. Fərq bu konteynerin necə istifadə olunmasıdır. Dərsləri konteynerdən asılı etməkdən çəkinsək, aydın və açıq sinif API əldə edəcəyik.
PHP proqramlarında obyektlərin yaradılması problemi haqqında sizə demək istədiyim təkcə bu deyil. Prototip nümunəsi də var, biz Reflection API-nin istifadəsini nəzərə almadıq, xidmətlərin tənbəl yüklənməsi problemini və bir çox digər nüansları bir kənara qoyduq. Məqalə kifayət qədər uzun oldu, ona görə də yekunlaşdıracağam :)
Göstərmək istədim ki, Dependency Injection və digər nümunələr ümumi hesab edildiyi kimi mürəkkəb deyil.
Dependency Injection haqqında danışırıqsa, məsələn, bu nümunənin KISS tətbiqləri var

Gələcək verilənlər bazasının strukturuna toxunaraq. Bir başlanğıc edildi və biz geri çəkilə bilmərik və mən bu barədə düşünmürəm.

Bir az sonra verilənlər bazasına qayıdacağıq, lakin hələlik mühərrikimizin kodunu yazmağa başlayacağıq. Ancaq əvvəlcə bir az hardware. Başlayın.

Vaxtın başlanğıcı

Hazırda bizim həyata keçirmək istədiyimiz sistemin işləməsi ilə bağlı yalnız bəzi ideyalarımız və anlayışımız var, lakin hələ ki, həyata keçirilməsinin özü yoxdur. Bizim işləyəcəyimiz heç nə yoxdur: heç bir funksional imkanımız yoxdur - və xatırladığınız kimi, biz onu 2 hissəyə ayırdıq: daxili və xarici. Əlifba hərfləri tələb edir, lakin xarici funksionallıq daxili funksionallığı tələb edir - burada başlayacağıq.

Amma o qədər də sürətli deyil. Bunun işləməsi üçün bir az daha dərinə getmək lazımdır. Sistemimiz bir iyerarxiyanı təmsil edir və istənilən iyerarxik sistemin başlanğıcı var: Linux-da quraşdırma nöqtəsi, Windows-da lokal disk, dövlət sistemi, şirkət, təhsil müəssisəsi və s. Belə sistemin hər bir elementi kiməsə tabedir və bir neçə tabeçiliyə malik ola bilər və qonşularına və onların tabeliyində olanlara müraciət etmək üçün yuxarılardan və ya başlanğıcın özündən istifadə edir. İerarxik sistemin yaxşı nümunəsi nəsil ağacıdır: başlanğıc nöqtəsi seçilir - hansısa əcdad - və biz gedirik. Sistemimizdə, həmçinin filialları böyüdəcəyimiz bir başlanğıc nöqtəsinə ehtiyacımız var - modullar, plaginlər və s. Bizə bir növ interfeys lazımdır ki, onun vasitəsilə bütün modullarımız “əlaqələnir”. Sonrakı iş üçün biz " anlayışı ilə tanış olmalıyıq. dizayn nümunəsi" və onların bir neçə tətbiqi.

Dizayn Nümunələri

Bunun nə olduğu və hansı növlərin olduğu haqqında çoxlu məqalələr var; mövzu olduqca zərifdir və sizə yeni bir şey deməyəcəyəm. Sevimli Wiki-də bu mövzu ilə bağlı məlumat var: slaydlı karet və bir az daha.

Dizayn naxışlarına tez-tez dizayn nümunələri və ya sadəcə naxışlar da deyilir (ingilis dilindən tərcümədə "naxış" deməkdir). Daha sonra məqalələrdə naxışlar haqqında danışarkən dizayn nümunələrini nəzərdə tutacağam.

Hər cür qorxulu (və o qədər də qorxulu olmayan) naxış adlarının böyük siyahısından indiyə qədər bizi yalnız ikisi maraqlandırır: qeyd dəftəri və singleton.

Qeydiyyat (və ya qeydiyyatdan keçin) müəyyən obyektlər toplusunu əlavə edə və silə və onlardan hər hansı birinə və onun imkanlarına çıxış əldə edə biləcəyiniz müəyyən massivdə işləyən nümunədir.

Tənha (və ya singleton) sinifin yalnız bir nümunəsinin mövcud ola biləcəyini təmin edən nümunədir. Onu kopyalamaq, yatdırmaq və ya oyatmaq mümkün deyil (PHP sehrindən danışırıq: __clone(), __sleep(), __wakeup()). Singleton qlobal giriş nöqtəsinə malikdir.

Təriflər tam və ya ümumiləşdirilməmişdir, lakin başa düşmək üçün bu kifayətdir. Onsuz da onlara ayrıca ehtiyacımız yoxdur. Biz bu nümunələrin hər birinin imkanları ilə maraqlanırıq, lakin bir sinifdə: belə bir nümunə deyilir singleton reyestri və ya Singleton Registry.

Bu bizə nə verəcək?
  • İstənilən vaxt obyektləri əlavə edə və kodun istənilən yerindən istifadə edə biləcəyimiz reyestrin tək bir nümunəsinə sahib olacağımıza zəmanət veriləcək;
  • onu kopyalamaq və PHP dilinin digər arzuolunmaz (bu halda) sehrindən istifadə etmək mümkün olmayacaq.

Bu mərhələdə başa düşmək kifayətdir ki, vahid reyestr bizə sistemin modul strukturunu həyata keçirməyə imkan verəcək, bu da məqsədləri müzakirə edərkən bizim istədiyimizdir, qalanını isə inkişaf irəlilədikcə başa düşəcəksiniz.

Yaxşı, kifayət qədər sözlər, yaradaq!

İlk sətirlər

Bu sinif nüvənin funksionallığına aid olacağından, biz layihəmizin kökündə nüvə modullarının bütün siniflərini yerləşdirəcəyimiz core adlı bir qovluq yaradaraq başlayacağıq. Biz registrdən başlayırıq, ona görə də faylı registry.php adlandıraq

Maraqlı istifadəçinin brauzer xəttinə faylımızın birbaşa ünvanını daxil etməsi bizi maraqlandırmır, ona görə də özümüzü bundan qorumalıyıq. Bu məqsədə çatmaq üçün sadəcə yoxlayacağımız əsas icra olunan faylda müəyyən sabiti təyin etməliyik. İdeya yeni deyil, xatırladığım qədəri Joomla-da istifadə edilmişdir. Bu sadə və işləyən bir üsuldur, buna görə də burada velosipedlər olmadan edə bilərik.

Bağlı bir şeyi qoruduğumuz üçün _PLUGSECURE_ sabitini çağıracağıq:

Əgər (!defined("_PLUGSECURE_")) ( die("Birbaşa modul çağırışı qadağandır!"); )

İndi bu fayla birbaşa daxil olmağa çalışsanız, faydalı heç nə çıxmayacaq, yəni məqsədə nail olunub.

Sonra, bütün modullarımız üçün müəyyən bir standart təyin etməyi təklif edirəm. Mən hər bir modulu onun haqqında bəzi məlumatları qaytaracaq funksiya ilə təmin etmək istəyirəm, məsələn, modulun adı və bu funksiya sinifdə tələb olunmalıdır. Bu məqsədə çatmaq üçün aşağıdakıları yazırıq:

StorableObject interfeysi (ictimai statik funksiya getClassName(); )

Bunun kimi. İndi funksiyası olmayan hər hansı bir sinfi birləşdirsək getClassName() bir səhv mesajı görəcəyik. Hələlik buna diqqət yetirməyəcəyəm, bu, heç olmasa sınaqdan keçirmək və aradan qaldırmaq üçün daha sonra bizim üçün faydalı olacaq.

Subaylar reyestrimizin özünün sinfinin vaxtıdır. Biz sinfi və onun bəzi dəyişənlərini elan etməklə başlayacağıq:

Sinif Qeydiyyatı StorableObject ( //modul adı oxuna bilən özəl statik $className = "Registri"; //registr nümunəsi özəl statik $instansiya; //obyektlər massivi şəxsi statik $objects = massiv();

Hələlik hər şey məntiqlidir və başa düşüləndir. İndi, xatırladığınız kimi, bizim singleton xassələri olan bir reyestrimiz var, ona görə də dərhal reyestrlə bu şəkildə işləməyə imkan verəcək bir funksiya yazaq:

İctimai statik funksiya singleton() ( if(!isset(self::$instance)) ( $obj = __CLASS__; self::$instance = new $obj; ) qaytar self::$instance; )

Sözün əsl mənasında: funksiya reyestrimizin bir nümunəsinin olub-olmadığını yoxlayır: əgər yoxdursa, onu yaradır və qaytarır; əgər artıq mövcuddursa, sadəcə onu qaytarır. Bu halda, bəzi sehrlərə ehtiyacımız yoxdur, ona görə də qorunmaq üçün onu özəl elan edəcəyik:

Şəxsi funksiya __construct()() özəl funksiya __clone()() özəl funksiya __wakeup()() özəl funksiya __sleep() ()

İndi reyestrimizə obyekt əlavə etmək üçün bir funksiyaya ehtiyacımız var - bu funksiya tənzimləyici adlanır və mən sehrdən necə istifadə edə biləcəyimizi göstərmək və obyekt əlavə etmək üçün alternativ bir yol təqdim etmək üçün onu iki şəkildə həyata keçirmək qərarına gəldim. Birinci üsul standart funksiyadır, ikincisi birincisini __set() sehri vasitəsilə yerinə yetirir.

//$object - əlaqəli obyektə gedən yol //$key - reyestrdəki obyektə giriş açarı ictimai funksiya addObject($key, $object) ( require_once($object); //obyektlər massivində obyekt yaradın self::$objects[ $key] = new $key(self::$instance); ) //sehrli ictimai funksiya vasitəsilə alternativ üsul __set($key, $object) ( $this->addObject($key, $) obyekt);)

İndi reyestrimizə obyekt əlavə etmək üçün iki növ qeyddən istifadə edə bilərik (tutaq ki, biz artıq $registr reyestrini yaratmışıq və config.php faylını əlavə etmək istəyirik):

$registry->addObject("config", "/core/config.php"); //müntəzəm metod $registry->config = "/core/config.php"; //PHP sehrli funksiyası vasitəsilə __set()

Hər iki giriş eyni funksiyanı yerinə yetirəcək - onlar faylı birləşdirəcək, sinfin nümunəsini yaradacaq və açarla registrdə yerləşdirəcək. Burada bir vacib məqam var ki, biz bunu gələcəkdə unutmamalıyıq: registrdəki obyekt açarı əlaqəli obyektdəki sinif adına uyğun gəlməlidir. Koda yenidən baxsanız, bunun səbəbini anlayacaqsınız.

Hansı qeyddən istifadə etmək sizə bağlıdır. Mən sehrli üsulla yazmağa üstünlük verirəm - bu "daha gözəl" və daha qısadır.

Beləliklə, biz obyekt əlavə etməyi sıraladıq, indi bizə qoşulmuş obyektə açarla daxil olmaq üçün funksiya lazımdır - alıcı. Mən də onu təyinediciyə bənzər iki funksiya ilə həyata keçirdim:

//registrdən obyekti əldə edin //$key - massivdəki açar getObject($key) ( //əgər (is_object(self::$objects[$key])) dəyişənin obyekt olub-olmadığını yoxlayın) ( //əgər belədirsə, biz bu obyekti qaytarırıq self::$objects[$key]; ) ) //sehrli ictimai funksiya vasitəsilə oxşar üsul __get($key) ( if (is_object(self::$objects[$) açar])) ( özünü qaytarın: :$objects[$key]; ) )

Ayarlayıcıda olduğu kimi, obyektə giriş əldə etmək üçün bizim 2 ekvivalent girişimiz olacaq:

$registr->getObject("konfiqurasiya"); //müntəzəm metod $registry->config; //PHP sehrli funksiyası vasitəsilə __get()

Diqqətli oxucu dərhal sual verəcək: niyə __set() sehrli funksiyasında mən sadəcə adi (sehrli olmayan) obyekt əlavə funksiyasını çağırıram, lakin __get() alıcısında eyni zəng əvəzinə getObject() funksiya kodunu kopyalayıram? Düzünü desəm, bu suala kifayət qədər dəqiq cavab verə bilmərəm, sadəcə deyim ki, digər modullarda __get() sehri ilə işləyərkən problemlərlə üzləşdim, lakin kodu “baş-üstə” yenidən yazarkən belə problemlər yoxdur.

Bəlkə də buna görə məqalələrdə tez-tez PHP-nin sehrli metodlarına qarşı iradlar və onlardan istifadə etməmək üçün tövsiyələr görürdüm.

"Bütün sehrlərin bir qiyməti var." © Rumplestiltskin

Bu mərhələdə reyestrimizin əsas funksionallığı artıq hazırdır: biz həm adi metodlardan istifadə etməklə, həm də PHP dilinin sehrli metodlarından istifadə etməklə reyestrin vahid instansiyasını yarada, obyektlər əlavə edə və onlara daxil ola bilərik. "Bəs silinmə?"— hələlik bu funksiyaya ehtiyacımız olmayacaq və gələcəkdə nəyinsə dəyişəcəyinə əmin deyiləm. Sonda biz həmişə lazımi funksionallığı əlavə edə bilərik. Ancaq indi reyestrimizin bir nümunəsini yaratmağa çalışsaq,

$registry = Registry::singleton();

xəta alacağıq:

Ölümcül səhv: Sinif Reyestrində 1 mücərrəd metod var və buna görə də mücərrəd elan edilməli və ya qalan metodları (StorableObject::getClassName) tətbiq etməlidir ...

Hamısı ona görə ki, biz tələb olunan funksiyanı yazmağı unutmuşuq. Yadınızdadırsa, ən əvvəl modulun adını qaytaran funksiyadan danışmışdım? Tam funksionallıq üçün əlavə edilməli olan budur. Bu sadədir:

İctimai statik funksiya getClassName() ( özünü qaytarın::$className; )

İndi səhvlər olmamalıdır. Mən daha bir funksiya əlavə etməyi təklif edirəm, bu tələb olunmur, lakin gec-tez bu işə yaraya bilər, biz bundan gələcəkdə yoxlama və sazlama üçün istifadə edəcəyik. Funksiya reyestrimizə əlavə edilmiş bütün obyektlərin (modulların) adlarını qaytaracaq:

İctimai funksiya getObjectsList() ( //qaytaracağımız massiv $names = array(); //foreach(self::$objects as $obj) obyektlər massivindən hər bir obyektin adını əldə edin) ( $adlar = $ obj->getClassName() ; ) //registr modulunun adını array_push($names, self::getClassName()) massivinə əlavə edin; //və $adları qaytarın; )

Hamısı budur. Bu, reyestri tamamlayır. Onun işini yoxlayaq? Yoxlama zamanı bir şey bağlamalıyıq - konfiqurasiya faylı olsun. Yeni core/config.php faylı yaradın və reyestrimizin tələb etdiyi minimum məzmunu əlavə edin:

//sabiti yoxlamağı unutmayın, əgər (!defined("_PLUGSECURE_")) ( die("Birbaşa modul çağırışı qadağandır!"); ) class Config ( //modul adı, oxuna bilən şəxsi statik $className = "Config "; ictimai statik funksiya getClassName() ( özünü qaytarın::$className; ) )

Belə bir şey. İndi yoxlamanın özünə keçək. Layihəmizin kök hissəsində index.php faylı yaradın və ona aşağıdakı kodu yazın:

Müəyyən et("_PLUGSECURE_", doğru); //obyektlərə birbaşa daxil olmaqdan qorunmaq üçün konstant təyin edildi require_once "/core/registry.php"; //registri birləşdirdi $registr = Registry::singleton(); //registr təkton nümunəsi yaratdı $registry->config = "/core/config.php"; //bu günə qədər yararsız olan konfiqurasiyamızı birləşdirin //əlaqəli modulların adlarını göstər echo " Əlaqədar"; foreach ($registr->

  • ". $adlar."
  • "; }

    Və ya hələ də sehrdən qaçırsınızsa, 5-ci sətir alternativ bir üsulla əvəz edilə bilər:

    Müəyyən et("_PLUGSECURE_", doğru); //obyektlərə birbaşa daxil olmaqdan qorunmaq üçün konstant təyin edildi require_once "/core/registry.php"; //registri birləşdirdi $registr = Registry::singleton(); //registr təkton nümunəsi yaratdı $registry->addObject("config", "/core/config.php"); //bu günə qədər yararsız olan konfiqurasiyamızı birləşdirin //əlaqəli modulların adlarını göstər echo " Əlaqədar"; foreach ($registry->getObjectsList() kimi $names) ( echo "

  • ". $adlar."
  • "; }

    İndi brauzeri açın və ünvan çubuğuna http://localhost/index.php və ya sadəcə http://localhost/ yazın. (standart Open Server və ya oxşar veb server parametrlərindən istifadə edirsinizsə, müvafiqdir)

    Nəticədə belə bir şey görməliyik:

    Gördüyünüz kimi, heç bir səhv yoxdur, yəni hər şey işləyir, buna görə sizi təbrik edirəm :)

    Bu gün biz bununla dayanacağıq. Növbəti məqalədə verilənlər bazasına qayıdacağıq və MySQL SUDB ilə işləmək üçün sinif yazacağıq, onu registrə qoşacağıq və işi praktikada yoxlayacağıq. görüşənədək!

    Həyatımızda tez-tez istifadə olunan naxışlar, daha çox nümunə, daha az su haqqında qısaca yazmaq qərarına gəldim.

    Singleton

    “Tək”in əsas məqamı odur ki, “Mənə telefon qovşağı lazımdır” deyəndə “ora artıq tikilib” deyəcəklər, “yenə tikək” yox. "Yalnız" həmişə təkdir.

    Class Singleton ( private static $instance = null; private function __construct())( /* ... @return Singleton */ ) // Yeni Singleton özəl funksiyası __clone() ( /* ... @return Singleton vasitəsilə yaradılmadan qorunur. * / ) // Şəxsi funksiyanı klonlaşdırmaqla yaradılışdan qorunmaq ) ) ( self::$instance = new self; ) return self::$instance; ) )

    Reyestr (reyestr, qeydlər jurnalı)

    Adından da göründüyü kimi, bu nümunə onda yerləşdirilən qeydləri saxlamaq və müvafiq olaraq tələb olunduqda bu qeydləri (adı ilə) qaytarmaq üçün nəzərdə tutulmuşdur. Telefon stansiyasının timsalında bu, sakinlərin telefon nömrələrinə münasibətdə reyestrdir.

    Sinif Qeydiyyatı ( şəxsi $registry = massiv(); ictimai funksiyalar dəsti($key, $object) ( $this->registr[$key] = $object; ) ictimai funksiya get($key) ($this->registrini qaytarın [$key]; ))

    Singleton Registry- ilə qarışdırmayın)

    “Reyestr” çox vaxt “tənha” olur, lakin həmişə belə olmamalıdır. Məsələn, biz mühasibatlıq şöbəsində bir neçə jurnal yarada bilərik, birində “A”dan “M”-ə, digərində “N”-dən “Z”-ə qədər işçilər var. Hər bir belə jurnal “registr” olacaq, lakin “tək” deyil, çünki artıq 2 jurnal var.

    Sinif SingletonRegistry ( şəxsi statik $instance = null; private $registry = array(); private function __construct() ( /* ... @return Singleton */ ) // Yeni Singleton özəl funksiyası __clone() () vasitəsilə yaradılmadan qoruyun. * ... @return Singleton */ ) // Şəxsi funksiyanı klonlaşdırmaqla yaradılışdan qoruyun __wakeup() ( /* ... @return Singleton */ ) // GetInstance() ictimai statik funksiyasını seriyadan çıxarmaqla yaradılışdan qoruyun ( əgər ( is_null(self::$instance)) ( self::$instance = new self; ) return self::$instance; ) public function set($key, $object) ( $this->registr[$key] = $ obyekt; ) ictimai funksiya get($key) ( $this->registr[$key]; ) qaytarın)

    Multiton (“təklər” hovuzu) və ya başqa sözləRegistry Singleton ) - Singleton Registry ilə qarışdırmayın

    Çox vaxt "qeydiyyat" xüsusi olaraq "subayları" saxlamaq üçün istifadə olunur. Amma, çünki “reyestr” nümunəsi “generativ nümunə” deyil, mən “registr”i “tək ton”la bağlı nəzərdən keçirmək istərdim.Buna görə bir nümunə ilə gəldik Multiton, görəÖzündə bu, bir neçə “tək”dən ibarət “reyestr”dir ki, hər birinin özünə daxil ola biləcəyi öz “adı” var.

    Qısa: bu sinfin obyektlərini yaratmağa imkan verir, ancaq obyekti adlandırdığınız halda. Heç bir real həyat nümunəsi yoxdur, amma İnternetdə aşağıdakı nümunəni tapdım:

    Sinif verilənlər bazası ( şəxsi statik $nümunələr = massiv(); özəl funksiya __construct() ( ) özəl funksiya __clone() ( ) ictimai statik funksiya getInstance($key) (if(!array_key_mövcuddur($key, self::$nümunələr)) ( self::$instances[$key] = new self(); ) return self::$instances[$key]; ) ) $master = Database::getInstance("master"); var_dump($master); // obyekt(Verilənlər bazası)#1 (0) ( ) $logger = Database::getInstance("logger"); var_dump($logger); // obyekt(Verilənlər bazası)#2 (0) ( ) $masterDupe = Database::getInstance("master"); var_dump($masterDupe); // obyekt(Verilənlər bazası)#1 (0) ( ) // Fatal xəta: Etibarsız kontekstdən şəxsi verilənlər bazasına zəng::__construct() $dbFatalError = new Database(); // PHP Fatal xətası: Şəxsi verilənlər bazasına zəng::__clone() $dbCloneError = $masterDupe klon;

    Obyekt hovuzu

    Əsasən bu modeldir yalnız obyektləri saxlayan "reyestr", heç bir sətir, massiv və s. məlumat növləri.

    Zavod

    Naxışın mahiyyəti onun adı ilə demək olar ki, tamamilə təsvir edilmişdir. Bəzi əşyaları, məsələn, şirə qutuları almaq lazım olduqda, onların fabrikdə necə hazırlandığını bilməyə ehtiyac yoxdur. Siz sadəcə olaraq “mənə bir karton portağal suyu verin” deyirsiniz və “zavod” sizə lazım olan paketi qaytarır. Necə? Bütün bunlara zavodun özü qərar verir, məsələn, artıq mövcud standartı “nüsxələyir”. "Zavodun" əsas məqsədi, zəruri hallarda şirə qablaşdırmasının "görünüşü" prosesini dəyişdirməyə imkan verməkdir və istehlakçıya bu barədə heç nə deməyə ehtiyac yoxdur ki, o, tələb edə bilsin. əvvəlki kimi. Bir qayda olaraq, bir fabrik yalnız bir növ “məhsul”un “istehsalı” ilə məşğul olur. Avtomobil şinlərinin istehsalını nəzərə alaraq “şirə zavodu” yaratmaq tövsiyə edilmir. Həyatda olduğu kimi, fabrik nümunəsi çox vaxt tək bir şəxs tərəfindən yaradılır.

    Mücərrəd sinfi AnimalAbstract ( qorunan $növlər; ictimai funksiya getSpecies() ( $this->növləri qaytarın; ) ) sinfi Cat AnimalAbstract-ı genişləndirir ( qorunan $növlər = "pişik"; ) sinfi Dog AnimalAbstract-ı genişləndirir ( qorunan $növlər = "it"; ) sinif AnimalFactory ( ictimai statik funksiya zavodu($animal) ( keçid ($animal) ( "pişik" halı: $obj = yeni Pişik(); fasilə; "it" halda: $obj = yeni İt(); fasilə; default : throw new Exception("Heyvan fabriki "" növünün heyvanını yarada bilmədi. $animal . """, 1000); ) return $obj; ) ) $cat = AnimalFactory::factory("cat"); // obyekt(Cat)#1 echo $cat->getSpecies(); // cat $dog = AnimalFactory::factory("it"); // object(Dog)#1 echo $dog->getSpecies(); // it $hippo = AnimalFactory::factory("hippopotamus"); // Bu İstisna atacaq

    Nəzərinizə çatdırım ki, zavod üsulu da bir naxışdır, ona Fabrik üsulu deyirlər.

    İnşaatçı (inşaatçı)

    Beləliklə, biz artıq başa düşdük ki, "Zavod" içki satışı avtomatıdır, artıq hər şey hazırdır və siz sadəcə sizə lazım olanı söyləyirsiniz. “İnşaatçı” bu içkiləri istehsal edən və bütün mürəkkəb əməliyyatları özündə cəmləşdirən zavoddur və tələbdən asılı olaraq mürəkkəb obyektləri sadələrdən (qablaşdırma, etiket, su, ləzzət və s.) yığa bilir.

    Class Bottle ( ictimai $name; ictimai $litr; ) /** * bütün qurucular */ interfeysi olmalıdır BottleBuilderInterface ( ictimai funksiya setName(); ictimai funksiya setLiters(); ictimai funksiya getResult(); ) sinif CocaColaBuilder BottleBuilderInterface ( şəxsi $) tətbiq edir şüşə; ictimai funksiya __construct() ( $this->bottle = new Bottle(); ) ictimai funksiya setName($value) ($this->bottle->name = $value; ) ictimai funksiya setLiters($value) ($ this->bottle->litr = $value; ) ictimai funksiya getResult() ( $this->bottle; ) qaytarın ) $juice = new CocaColaBuilder(); $juice->setName("Coca-Cola Light"); $şirə->setLitrlər(2); $juice->getResult();

    Prototip

    Fabrikə bənzəyən o, həm də obyektlərin yaradılmasına xidmət edir, lakin bir qədər fərqli yanaşma ilə. Özünüzü bir barda təsəvvür edin, pivə içirdiniz və pivəniz tükənir, meyxanaçıya deyirsiniz - mənə eyni növdən başqa birini hazırlayın. Barmen öz növbəsində içdiyiniz pivəyə baxır və xahiş etdiyiniz kimi surətini çıxarır. PHP-də artıq bu nümunənin tətbiqi var, buna .

    $newJuice = $şirə klon;

    Tənbəl başlatma

    Məsələn, bir müdir müxtəlif fəaliyyət növləri üçün hesabatların siyahısını görür və bu hesabatların artıq mövcud olduğunu düşünür, lakin əslində yalnız hesabatların adları göstərilir və hesabatların özləri hələ yaradılmayıb və yalnız yaradılacaq. sifariş əsasında (məsələn, Hesabata baxmaq düyməsini klikləməklə). Tənbəl inisializasiyanın xüsusi halı obyektin ona daxil olduğu anda yaradılmasıdır. Vikipediyada maraqlı tapa bilərsiniz, amma... nəzəriyyəyə görə, php-də düzgün nümunə, məsələn, funksiya olardı

    Adapter və ya sarğı (adapter, sarğı)

    Bu naxış onun adına tam uyğundur. "Sovet" fişinin Avro rozetkası vasitəsilə işləməsi üçün adapter tələb olunur. "Adapter" məhz bunu edir - o, bir-biri ilə birbaşa işləyə bilməyən digər ikisi arasında ara obyekt kimi xidmət edir. Tərifə baxmayaraq, praktikada hələ də Adapter və Sarmalayıcı arasındakı fərqi görürəm.

    Class MyClass ( public function methodA() () ) class MyClassWrapper ( ictimai funksiya __construct())( $this->myClass = new MyClass(); ) ictimai funksiya __call($name, $arquments)( Log::info(" Siz $name metoduna zəng etmək üzrəsiniz."); return call_user_func_array(array($this->myClass, $name), $arquments); ) ) $obj = new MyClassWrapper(); $obj->metodA();

    Asılılıq inyeksiyası

    Asılılıq inyeksiyası bəzi funksionallıq üçün məsuliyyətin bir hissəsini digər obyektlərə keçirməyə imkan verir. Məsələn, yeni kadrlar işə götürməli olsaq, onda biz öz kadrlar şöbəmizi yarada bilmərik, lakin işə qəbul şirkətindən asılılığı tətbiq edirik ki, bu da öz növbəsində bizim ilk müraciətimizdə “bizə bir insan lazımdır” ya işçi kimi işləyəcək. HR departamentinin özü və ya bu xidmətləri təmin edəcək başqa bir şirkət ("xidmət lokatoru" istifadə edərək) tapacaq.
    "Asılılıq inyeksiyası" ümumi funksionallığı itirmədən şirkətin ayrı-ayrı hissələrini dəyişdirməyə və dəyişdirməyə imkan verir.

    Sinif AppleJuice () // bu üsul Asılılıq inyeksiya modelinin primitiv tətbiqidir və daha sonra bu funksiyanı görəcəksiniz getBottleJuice())( $obj = new Alma şirəsi Alma şirəsi)( return $obj; ) ) $bottleJuice = getBottleJuice();

    İndi təsəvvür edin ki, biz artıq alma şirəsi yox, portağal suyu istəyirik.

    Sinif AppleJuice() Sinfi Portağal şirəsi() // bu üsul getBottleJuice())( $obj = yeni Asılılıq inyeksiya funksiyasını həyata keçirir. Portağal şirəsi; // obyekti yoxlayın, əgər onlar bizə pivə vurdularsa (pivə şirə deyil) if($obj instanceof Portağal şirəsi)( $obj qaytarın; ) )

    Gördüyünüz kimi, biz təkcə şirənin növünü deyil, həm də şirənin növünün yoxlanışını dəyişməli olduq ki, bu da çox rahat deyil. Asılılığın inversiya prinsipindən istifadə etmək daha düzgündür:

    Interface Juice () Class AppleJuice tətbiq edir Juice () Class OrangeJuice Juice () funksiyası getBottleJuice())( $obj = new OrangeJuice; // obyekti yoxlayın, əgər onlar bizə pivə keçiriblər (pivə şirə deyil) if($obj) misal Şirə)( $obj qaytarın; ) )

    Asılılıq inversiyasını bəzən Asılılıq inyeksiyası ilə qarışdırırlar, lakin onları qarışdırmağa ehtiyac yoxdur, çünki Asılılığın inversiyası nümunə deyil, prinsipdir.

    Xidmət Lokator

    "Xidmət Lokatoru" "Asılılıq Injection"ın həyata keçirilməsi üsuludur. O, başlanğıc kodundan asılı olaraq müxtəlif növ obyektləri qaytarır. Tapşırığımız inşaatçı, fabrik və ya başqa bir şey tərəfindən yaradılmış şirə paketimizi alıcının istədiyi yerə çatdırmaqdan ibarət olsun. Lokatora “bizə çatdırılma xidməti göstərin” deyirik və xidmətdən suyu istədiyiniz ünvana çatdırmağı xahiş edirik. Bu gün bir xidmət var, sabah başqa bir xidmət ola bilər. Bunun hansı xüsusi xidmət olmasının bizim üçün əhəmiyyəti yoxdur, bu xidmətin ona dediklərimizi və harada dediyimizi çatdıracağını bilmək bizim üçün vacibdir. Öz növbəsində xidmətlər “Çatdır<предмет>haqqında<адрес>».

    Əgər real həyatdan danışırıqsa, onda çox güman ki, Xidmət Lokatorunun yaxşı nümunəsi PDO PHP genişlənməsi ola bilər, çünki Bu gün biz MySQL verilənlər bazası ilə işləyirik, sabah isə PostgreSQL ilə işləyə bilərik. Artıq başa düşdüyünüz kimi, sinifimiz üçün məlumatlarını hansı verilənlər bazasına göndərməsinin əhəmiyyəti yoxdur, bunu edə bilməsi vacibdir.

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

    Dependency injection və Service Locator arasındakı fərq

    Hələ fərq etməmisinizsə, izah etmək istərdim. Asılılıq inyeksiyası Nəticə etibarı ilə o, xidməti (harasa bir şey çatdıra bilən) deyil, məlumatlarından istifadə etdiyi obyekti qaytarır.



    Əlaqədar nəşrlər