Օբյեկտների սկզբնավորման խնդիրը PHP-ում OOP հավելվածներում: Լուծում գտնելը ռեեստրի, գործարանային մեթոդի, ծառայության տեղորոշիչի և կախվածության ներարկման ձևերի միջոցով: OOP օրինաչափություններ օրինակներով և նկարագրություններով Հեղինակավոր գրանցում php

Օբյեկտների սկզբնավորման խնդիրը PHP-ում OOP հավելվածներում: Լուծում գտնել ռեեստրի, գործարանային մեթոդի, ծառայության տեղորոշիչի և կախվածության ներարկման ձևերի միջոցով

Պարզապես պատահում է, որ ծրագրավորողները համախմբում են հաջող լուծումները դիզայնի օրինաչափությունների տեսքով: Կաղապարների վերաբերյալ գրականությունը շատ է։ Էրիխ Գամմայի, Ռիչարդ Հելմի, Ռալֆ Ջոնսոնի և Ջոն Վլիսայդսի «Դիզայնի ձևանմուշներ» գիրքը և, հավանաբար, Մարտին Ֆաուլերի «Ձեռնարկությունների կիրառական ճարտարապետության նախշերը» գիրքը, անշուշտ, դասական են համարվում: Լավագույնը, ինչ ես կարդացել եմ օրինակներով: PHP-ում - սա: Պատահում է, որ այս ամբողջ գրականությունը բավականին բարդ է այն մարդկանց համար, ովքեր նոր են սկսել տիրապետել OOP-ին: Այսպիսով, ես միտք ունեցա ներկայացնելու որոշ օրինաչափություններ, որոնք ինձ առավել օգտակար են համարում շատ պարզեցված ձևով: բառերով, այս հոդվածը իմ առաջին փորձն է մեկնաբանելու դիզայնի նախշերը KISS ոճով:
Այսօր մենք կխոսենք այն մասին, թե ինչ խնդիրներ կարող են առաջանալ օբյեկտների սկզբնականացման հետ կապված OOP հավելվածում և ինչպես կարող եք օգտագործել որոշ հայտնի դիզայնի օրինաչափություններ այս խնդիրները լուծելու համար:

Օրինակ

Ժամանակակից OOP հավելվածն աշխատում է տասնյակ, հարյուրավոր և երբեմն հազարավոր օբյեկտների հետ: Դե, եկեք ավելի սերտ նայենք, թե ինչպես են այս օբյեկտները սկզբնավորվում մեր հավելվածներում: Օբյեկտների սկզբնավորումը միակ ասպեկտն է, որը մեզ հետաքրքրում է այս հոդվածում, ուստի ես որոշեցի բաց թողնել բոլոր «լրացուցիչ» իրականացումը:
Ենթադրենք, մենք ստեղծել ենք սուպեր-դյուպեր օգտակար դաս, որը կարող է GET հարցում ուղարկել կոնկրետ URI-ին և վերադարձնել HTML սերվերի պատասխանից: Որպեսզի մեր դասը շատ պարզ չթվա, թող այն նույնպես ստուգի արդյունքը և բացառություն անի, եթե սերվերը պատասխանի «սխալ»:

Class Grabber (public function get($url) (/** վերադարձնում է HTML կոդը կամ գցում է բացառություն */) )

Եկեք ստեղծենք մեկ այլ դաս, որի օբյեկտները պատասխանատու կլինեն ստացված HTML-ի զտման համար։ Զտիչ մեթոդը վերցնում է HTML կոդը և CSS ընտրիչը որպես արգումենտ, և այն վերադարձնում է տվյալ ընտրիչի համար հայտնաբերված տարրերի զանգված:

Դաս HtmlExtractor ( հանրային ֆունկցիայի զտիչ ($html, $selector) (/** վերադարձնում է զտված տարրերի զանգված */) )

Հիմա պատկերացրեք, որ մենք պետք է Google-ում որոնման արդյունքներ ստանանք տվյալ հիմնաբառերի համար: Դա անելու համար մենք կներկայացնենք մեկ այլ դաս, որը կօգտագործի Grabber դասը՝ հարցում ուղարկելու համար, իսկ HtmlExtractor դասը՝ անհրաժեշտ բովանդակությունը հանելու համար։ Այն նաև կպարունակի URI-ի կառուցման տրամաբանություն, ստացված HTML-ը զտելու և ստացված արդյունքների մշակման ընտրիչ։

Դաս GoogleFinder ( անձնական $grabber; մասնավոր $filter; հանրային գործառույթ __construct() ( $this->grabber = new Grabber(); $this->filter = new HtmlExtractor(); ) հանրային գործառույթ գտնել ($searchString) ( /* * վերադարձնում է հիմնված արդյունքների զանգված */) )

Նկատե՞լ եք, որ Grabber և HtmlExtractor օբյեկտների սկզբնավորումը GoogleFinder դասի կոնստրուկտորում է: Եկեք մտածենք, թե որքան լավ է այս որոշումը:
Իհարկե, կոնստրուկտորում օբյեկտների ստեղծման կոշտ կոդավորումը լավ գաղափար չէ: Եվ ահա թե ինչու։ Նախ, մենք չենք կարողանա հեշտությամբ վերացնել Grabber դասը թեստային միջավայրում, որպեսզի խուսափենք իրական հարցում ուղարկելուց: Արդարության համար արժե ասել, որ դա կարելի է անել Reflection API-ի միջոցով։ Նրանք. Տեխնիկական հնարավորությունը կա, բայց դա հեռու է ամենահարմար և ակնհայտ ձևից։
Երկրորդ, նույն խնդիրը կառաջանա, եթե մենք ցանկանանք կրկին օգտագործել GoogleFinder-ի տրամաբանությունը Grabber-ի և HtmlExtractor-ի այլ ծրագրերի հետ։ Կախվածությունների ստեղծումը կոշտ կոդավորված է դասի կոնստրուկտորում: Եվ լավագույն դեպքում մենք կկարողանանք ժառանգել GoogleFinder-ը և անտեսել դրա կոնստրուկտորը: Եվ նույնիսկ այն դեպքում, միայն այն դեպքում, եթե գրավիչի և ֆիլտրի հատկությունների շրջանակը պաշտպանված է կամ հրապարակային:
Մի վերջին կետ, ամեն անգամ, երբ մենք ստեղծում ենք նոր GoogleFinder օբյեկտ, հիշողության մեջ կստեղծվի կախվածության նոր զույգ օբյեկտներ, չնայած մենք կարող ենք բավականին հեշտությամբ օգտագործել մեկ Grabber օբյեկտ և մեկ HtmlExtractor օբյեկտ մի քանի GoogleFinder օբյեկտներում:
Կարծում եմ, դուք արդեն հասկանում եք, որ կախվածության սկզբնավորումը պետք է տեղափոխվի դասից դուրս: Մենք կարող ենք պահանջել, որ արդեն պատրաստված կախվածությունները փոխանցվեն GoogleFinder դասի կոնստրուկտորին:

Դաս GoogleFinder ( անձնական $grabber; մասնավոր $filter; հանրային ֆունկցիա __construct(Grabber $grabber, HtmlExtractor $filter) ($this->grabber = $grabber; $this->filter = $filter; ) հանրային ֆունկցիայի գտնել ($searchString) (/** վերադարձնում է հիմնված արդյունքների զանգված */))

Եթե ​​մենք ցանկանում ենք այլ մշակողների հնարավորություն տալ ավելացնելու և օգտագործելու իրենց սեփական Grabber և HtmlExtractor իրականացումները, ապա մենք պետք է մտածենք նրանց համար ինտերֆեյսների ներդրման մասին: Այս դեպքում սա ոչ միայն օգտակար է, այլեւ անհրաժեշտ։ Ես կարծում եմ, որ եթե մենք նախագծում օգտագործում ենք միայն մեկ իրականացում և չենք ակնկալում ապագայում ստեղծել նորերը, ապա պետք է հրաժարվենք ինտերֆեյս ստեղծելուց։ Ավելի լավ է գործել ըստ իրավիճակի և կատարել պարզ ռեֆակտորինգ, երբ դրա կարիքն իսկապես կա։
Այժմ մենք ունենք բոլոր անհրաժեշտ դասերը և կարող ենք օգտագործել GoogleFinder դասը կարգավորիչում։

Դասի վերահսկիչ ( հանրային ֆունկցիայի գործողություն() ( /* Որոշ նյութեր */ $finder = նոր GoogleFinder(նոր Grabber(), նոր HtmlExtractor()); $results = $finder->

Ամփոփենք միջանկյալ արդյունքները։ Մենք շատ քիչ կոդ ենք գրել, և առաջին հայացքից ոչ մի վատ բան չենք արել: Բայց... ի՞նչ, եթե մեզ անհրաժեշտ լինի GoogleFinder-ի նման օբյեկտ օգտագործել մեկ այլ վայրում: Մենք ստիպված կլինենք կրկնօրինակել դրա ստեղծումը: Մեր օրինակում սա ընդամենը մեկ տող է, և խնդիրն այնքան էլ նկատելի չէ։ Գործնականում օբյեկտների սկզբնավորումը կարող է բավականին բարդ լինել և կարող է տևել մինչև 10 տող կամ նույնիսկ ավելին: Ծագում են նաև կոդերի կրկնօրինակմանը բնորոշ այլ խնդիրներ։ Եթե ​​վերամշակման գործընթացում դուք պետք է փոխեք օգտագործվող դասի անունը կամ օբյեկտի սկզբնավորման տրամաբանությունը, դուք պետք է ձեռքով փոխեք բոլոր տեղերը: Կարծում եմ դուք գիտեք, թե ինչպես է դա տեղի ունենում :)
Սովորաբար, կոշտ կոդով զբաղվում են պարզապես: Կրկնվող արժեքները սովորաբար ներառված են կազմաձևում: Սա թույլ է տալիս փոխել արժեքները կենտրոնական բոլոր վայրերում, որտեղ դրանք օգտագործվում են:

Ռեեստրի ձևանմուշ.

Այսպիսով, մենք որոշեցինք օբյեկտների ստեղծումը տեղափոխել կոնֆիգուրացիա: Արի անենք դա.

$registry = new ArrayObject(); $registry["grabber"] = new Grabber(); $registry["filter"] = նոր HtmlExtractor(); $registry["google_finder"] = նոր GoogleFinder($registry["grabber"], $registry["filter"]);
Մեզ մնում է միայն փոխանցել մեր ArrayObject-ը վերահսկիչին, և խնդիրը լուծված է:

Դասի վերահսկիչ ( մասնավոր $registry; հանրային գործառույթ __construct(ArrayObject $registry) ( $this->registry = $registry; ) հանրային գործառույթի գործողություն() ( /* Որոշ նյութեր */ $results = $this->registry["google_finder" ]->գտնել ("որոնման տող"); /* Արդյունքներով ինչ-որ բան արեք */ ) )

Մենք կարող ենք հետագայում զարգացնել Ռեեստրի գաղափարը: Ժառանգել ArrayObject-ը, ներառել օբյեկտների ստեղծումը նոր դասի ներսում, արգելել սկզբնավորումից հետո նոր օբյեկտներ ավելացնելը և այլն: Բայց իմ կարծիքով, տվյալ կոդը լիովին պարզ է դարձնում, թե որն է Ռեեստրի կաղապարը։ Այս օրինաչափությունը գեներացնող չէ, բայց այն որոշակի ճանապարհով է լուծում մեր խնդիրները: Ռեեստրը պարզապես կոնտեյներ է, որտեղ մենք կարող ենք պահել օբյեկտները և դրանք փոխանցել հավելվածի ներսում: Որպեսզի օբյեկտները հասանելի դառնան, մենք նախ պետք է ստեղծենք դրանք և գրանցենք դրանք այս կոնտեյներով: Դիտարկենք այս մոտեցման առավելություններն ու թերությունները:
Առաջին հայացքից մենք հասել ենք մեր նպատակին. Մենք դադարեցրինք դասերի անունների կոշտ կոդավորումը և օբյեկտներ ստեղծել մեկ տեղում: Մենք ստեղծում ենք օբյեկտներ մեկ օրինակով, ինչը երաշխավորում է դրանց կրկնակի օգտագործումը: Եթե ​​օբյեկտներ ստեղծելու տրամաբանությունը փոխվի, ապա հավելվածում միայն մեկ տեղ պետք է խմբագրվի։ Որպես բոնուս, մենք ստացանք ռեեստրի օբյեկտները կենտրոնացված կառավարելու հնարավորություն: Մենք հեշտությամբ կարող ենք ստանալ բոլոր առկա օբյեկտների ցանկը և դրանցով կատարել որոշ մանիպուլյացիաներ: Եկեք հիմա տեսնենք, թե ինչ կարող է մեզ դուր չգալ այս ձևանմուշում:
Նախ, մենք պետք է ստեղծենք օբյեկտը, նախքան այն գրանցելը գրանցամատյանում: Համապատասխանաբար, կա «ավելորդ առարկաներ» ստեղծելու մեծ հավանականություն, այսինքն. նրանք, որոնք կստեղծվեն հիշողության մեջ, բայց չեն օգտագործվի հավելվածում։ Այո, մենք կարող ենք դինամիկ կերպով օբյեկտներ ավելացնել ռեեստրում, այսինքն. ստեղծել միայն այն օբյեկտները, որոնք անհրաժեշտ են կոնկրետ հարցումը մշակելու համար: Այսպես թե այնպես, մենք ստիպված կլինենք ձեռքով վերահսկել դա: Համապատասխանաբար, ժամանակի ընթացքում այն ​​պահպանելը շատ դժվար կդառնա։
Երկրորդ, մենք ունենք նոր վերահսկիչ կախվածություն: Այո, մենք կարող ենք օբյեկտներ ստանալ ռեեստրի ստատիկ մեթոդի միջոցով, որպեսզի ռեեստրը չփոխանցենք կոնստրուկտորին: Բայց իմ կարծիքով, դուք չպետք է դա անեք: Ստատիկ մեթոդները նույնիսկ ավելի ամուր կապ են, քան օբյեկտի ներսում կախվածություն ստեղծելը և փորձարկման դժվարությունները (այս թեմայի շուրջ):
Երրորդ, վերահսկիչի ինտերֆեյսը մեզ ոչինչ չի ասում, թե ինչ օբյեկտներ է այն օգտագործում: Մենք կարող ենք վերահսկիչում ստանալ ռեեստրում առկա ցանկացած օբյեկտ: Մեզ համար դժվար կլինի ասել, թե որ օբյեկտներն է օգտագործում վերահսկիչը, քանի դեռ չենք ստուգել նրա ողջ սկզբնական կոդը։

Գործարանային մեթոդ

Registry-ի հետ կապված մեր ամենամեծ դժվարությունն այն է, որ օբյեկտը պետք է սկզբնավորվի, նախքան այն հասանելի լինի: Կոնֆիգուրացիայի մեջ օբյեկտը սկզբնավորելու փոխարեն, մենք կարող ենք առանձնացնել օբյեկտներ ստեղծելու տրամաբանությունը մեկ այլ դասի մեջ, որը մենք կարող ենք «խնդրել» կառուցել մեզ անհրաժեշտ օբյեկտը: Դասերը, որոնք պատասխանատու են օբյեկտների ստեղծման համար, կոչվում են գործարաններ։ Իսկ դիզայնի օրինաչափությունը կոչվում է Factory Method: Եկեք նայենք գործարանի օրինակին:

Class Factory ( հանրային ֆունկցիա getGoogleFinder() ( վերադարձնել նոր GoogleFinder($this->getGrabber(), $this->getHtmlExtractor()); ) մասնավոր ֆունկցիա getGrabber() ( վերադարձնել նոր Grabber(); ) մասնավոր ֆունկցիա getHtmlExtractor() ( վերադարձնել նոր HtmlFiletr();))

Որպես կանոն, արտադրվում են գործարաններ, որոնք պատասխանատու են մեկ տեսակի օբյեկտ ստեղծելու համար։ Երբեմն գործարանը կարող է ստեղծել հարակից օբյեկտների խումբ: Մենք կարող ենք օգտագործել քեշավորումը սեփականության մեջ՝ օբյեկտների վերստեղծումից խուսափելու համար:

Class Factory ( անձնական $finder; հանրային գործառույթ getGoogleFinder() ( if (null === $this->finder) ($this->finder = new GoogleFinder($this->getGrabber(), $this->getHtmlExtractor() );) վերադարձնել $this->finder;))

Մենք կարող ենք պարամետրացնել գործարանային մեթոդը և սկզբնականացումը պատվիրակել այլ գործարանների՝ կախված մուտքային պարամետրից: Սա արդեն կլինի Abstract Factory կաղապար:
Եթե ​​մեզ անհրաժեշտ է մոդուլյարացնել հավելվածը, մենք կարող ենք պահանջել, որ յուրաքանչյուր մոդուլ տրամադրի իր սեփական գործարանները: Մենք կարող ենք հետագայում զարգացնել գործարանների թեման, բայց կարծում եմ, որ այս կաղապարի էությունը պարզ է։ Տեսնենք, թե ինչպես կօգտագործենք գործարանը կարգավորիչում:

Դասի վերահսկիչ ( մասնավոր $factory; հանրային ֆունկցիա __construct(Factory $factory) ( $this->factory = $factory; ) հանրային ֆունկցիայի գործողություն() ( /* Որոշ նյութեր */ $results = $this->factory->getGoogleFinder( )->գտնել ("որոնման տող"); /* Արդյունքներով ինչ-որ բան արեք */ ) )

Այս մոտեցման առավելությունները ներառում են դրա պարզությունը: Մեր օբյեկտները ստեղծվում են հստակորեն, և ձեր IDE-ն հեշտությամբ կհանգեցնի ձեզ այնտեղ, որտեղ դա տեղի է ունենում: Մենք նաև լուծել ենք Ռեգիստրի խնդիրը, որպեսզի հիշողության մեջ օբյեկտները ստեղծվեն միայն այն ժամանակ, երբ մենք «խնդրենք» գործարանին դա անել: Բայց մենք դեռ չենք որոշել, թե ինչպես պետք է մատակարարել անհրաժեշտ գործարանները վերահսկողներին։ Այստեղ կան մի քանի տարբերակներ: Դուք կարող եք օգտագործել ստատիկ մեթոդներ: Մենք կարող ենք թույլ տալ, որ վերահսկիչները իրենք ստեղծեն անհրաժեշտ գործարանները և չեղյալ համարեն copy-paste-ից ազատվելու մեր բոլոր փորձերը։ Դուք կարող եք ստեղծել գործարանների գործարան և փոխանցել միայն այն վերահսկիչին: Բայց կարգավորիչում առարկաներ ստանալը մի փոքր ավելի կբարդանա, և դուք պետք է կառավարեք կախվածությունը գործարանների միջև: Բացի այդ, լիովին պարզ չէ, թե ինչ անել, եթե ցանկանում ենք օգտագործել մոդուլներ մեր հավելվածում, ինչպես գրանցել մոդուլների գործարանները, ինչպես կառավարել գործարանների միջև կապերը տարբեր մոդուլներից: Ընդհանուր առմամբ, մենք կորցրել ենք գործարանի հիմնական առավելությունը՝ օբյեկտների բացահայտ ստեղծումը։ Եվ մենք դեռ չենք լուծել «ներածական» վերահսկիչի ինտերֆեյսի խնդիրը:

Ծառայության տեղորոշիչ

Service Locator կաղապարը թույլ է տալիս լուծել գործարանների մասնատվածության բացակայությունը և կառավարել օբյեկտների ստեղծումը ավտոմատ և կենտրոնացված: Եթե ​​մտածենք դրա մասին, կարող ենք ներդնել լրացուցիչ աբստրակցիոն շերտ, որը պատասխանատու կլինի մեր հավելվածում օբյեկտներ ստեղծելու և այդ օբյեկտների միջև հարաբերությունները կառավարելու համար։ Որպեսզի այս շերտը կարողանա մեզ համար օբյեկտներ ստեղծել, մենք պետք է նրան գիտելիքներ տանք, թե ինչպես դա անել:
Ծառայության տեղորոշիչի օրինաչափության պայմանները.
  • Ծառայությունը պատրաստի օբյեկտ է, որը կարելի է ձեռք բերել կոնտեյներով։
  • Ծառայության սահմանում – ծառայության սկզբնավորման տրամաբանություն:
  • Կոնտեյներ (Service Container) կենտրոնական օբյեկտ է, որը պահպանում է բոլոր նկարագրությունները և կարող է դրանց հիման վրա ծառայություններ ստեղծել։
Ցանկացած մոդուլ կարող է գրանցել իր ծառայության նկարագրությունները: Կոնտեյներից որոշակի ծառայություն ստանալու համար մենք ստիպված կլինենք բանալիով պահանջել այն: Ծառայությունների տեղորոշիչի ներդրման բազմաթիվ տարբերակներ կան, ամենապարզ տարբերակում մենք կարող ենք օգտագործել ArrayObject-ը որպես կոնտեյներ և փակում՝ որպես ծառայությունների նկարագրություն:

Class ServiceContainer-ը ընդլայնում է ArrayObject ( հանրային գործառույթը get($key) ( if (is_callable($this[$key])) ( return call_user_func($this[$key]); բանալին [$key]");))

Այնուհետև Սահմանումների գրանցումը կունենա հետևյալ տեսքը.

$container = new ServiceContainer(); $container["grabber"] = ֆունկցիա () ( վերադարձնել նոր Grabber(); ); $container["html_filter"] = ֆունկցիա () ( վերադարձնել նոր HtmlExtractor(); ); $container["google_finder"] = ֆունկցիան() օգտագործել ($container) ( վերադարձնել նոր GoogleFinder($container->get("grabber"), $container->get("html_filter")); );

Եվ վերահսկիչի օգտագործումը այսպիսին է.

Դասի վերահսկիչ ( մասնավոր $container; հանրային ֆունկցիա __construct(ServiceContainer $container) ( $this->container = $container; ) հանրային ֆունկցիայի գործողություն() ( /* Որոշ նյութեր */ $results = $this->container->get( "google_finder")->find("որոնման տող"); /* Արդյունքներով ինչ-որ բան արեք */ ) )

Ծառայության կոնտեյները կարող է լինել շատ պարզ, կամ կարող է լինել շատ բարդ: Օրինակ, Symfony Service Container-ը տրամադրում է բազմաթիվ հնարավորություններ՝ պարամետրեր, ծառայությունների շրջանակներ, ծառայությունների որոնում ըստ պիտակների, անունների, մասնավոր ծառայությունների, բոլոր ծառայություններն ավելացնելուց հետո կոնտեյների մեջ փոփոխություններ կատարելու հնարավորություն (կոմպիլյատորի անցումներ) և շատ ավելին: DIExtraBundle-ն ավելի է ընդլայնում ստանդարտ ներդրման հնարավորությունները:
Բայց վերադառնանք մեր օրինակին։ Ինչպես տեսնում եք, Service Locator-ը ոչ միայն լուծում է բոլոր նույն խնդիրները, ինչ նախորդ կաղապարները, այլև հեշտացնում է մոդուլների օգտագործումը իրենց ծառայության սահմանումներով:
Բացի այդ, շրջանակային մակարդակում մենք ստացել ենք վերացականության լրացուցիչ մակարդակ։ Մասնավորապես, փոխելով ServiceContainer::get մեթոդը, մենք կարող ենք, օրինակ, օբյեկտը փոխարինել պրոքսիով: Եվ վստահված անձի օբյեկտների կիրառման շրջանակը սահմանափակվում է միայն մշակողի երևակայությամբ: Այստեղ դուք կարող եք իրականացնել AOP պարադիգմը, LazyLoading և այլն:
Սակայն մշակողների մեծ մասը դեռ համարում է Service Locator-ը հակաօրինաչափություն: Որովհետեւ, տեսականորեն, մենք կարող ենք ունենալ նույնքան, այսպես կոչված Container Aware դասեր (այսինքն դասեր, որոնք պարունակում են հղում դեպի կոնտեյներ): Օրինակ՝ մեր Controller-ը, որի ներսում կարող ենք ցանկացած ծառայություն ստանալ։
Տեսնենք, թե ինչու է սա վատ:
Նախ, կրկին փորձարկում: Միայն թեստերում օգտագործվող դասերի համար ծաղրեր ստեղծելու փոխարեն դուք ստիպված կլինեք ծաղրել ամբողջ բեռնարկղը կամ օգտագործել իրական կոնտեյներ: Առաջին տարբերակը ձեզ չի սազում, քանի որ... դուք պետք է շատ ավելորդ կոդ գրեք թեստերում, երկրորդ, քանի որ այն հակասում է միավորի փորձարկման սկզբունքներին և կարող է հանգեցնել թեստերի պահպանման լրացուցիչ ծախսերի:
Երկրորդ՝ մեզ համար դժվար կլինի վերամշակել։ Փոխելով որևէ ծառայություն (կամ ServiceDefinition) կոնտեյներով, մենք ստիպված կլինենք ստուգել նաև բոլոր կախյալ ծառայությունները: Եվ այս խնդիրը հնարավոր չէ լուծել IDE-ի օգնությամբ։ Նման վայրեր գտնելն ամբողջ հավելվածում այնքան էլ հեշտ չի լինի։ Բացի կախյալ ծառայություններից, ձեզ հարկավոր է նաև ստուգել բոլոր այն վայրերը, որտեղ վերամշակված ծառայությունը ստացվում է կոնտեյներից:
Դե, երրորդ պատճառն այն է, որ կոնտեյներից ծառայությունների անվերահսկելի դուրսբերումը վաղ թե ուշ կբերի կոդի խառնաշփոթի և անհարկի շփոթության։ Սա դժվար է բացատրել, պարզապես պետք է ավելի ու ավելի շատ ժամանակ ծախսել՝ հասկանալու համար, թե ինչպես է աշխատում այս կամ այն ​​ծառայությունը, այլ կերպ ասած՝ կարող ես լիովին հասկանալ, թե ինչ է անում այն ​​կամ ինչպես է աշխատում դասը միայն կարդալով դրա ամբողջական կոդը:

Կախվածության ներարկում

Էլ ի՞նչ կարող եք անել՝ սահմանափակելու համար կոնտեյների օգտագործումը հավելվածում: Դուք կարող եք կառավարումը փոխանցել օգտվողի բոլոր օբյեկտների, ներառյալ կարգավորիչների, ստեղծման շրջանակին: Այլ կերպ ասած, օգտագործողի կոդը չպետք է կանչի կոնտեյների ստացման մեթոդը: Մեր օրինակում մենք կարող ենք կոնտեյների մեջ ավելացնել վերահսկիչի սահմանում.

$container["google_finder"] = function() use ($container) ( վերադարձնել նոր Controller(Grabber $grabber); );

Եվ ազատվեք կարգավորիչի կոնտեյներից.

Դասի վերահսկիչ ( անձնական $finder; հանրային գործառույթ __construct (GoogleFinder $finder) ( $this->finder = $finder; ) հանրային գործառույթի գործողություն() ( /* Որոշ նյութեր */ $results = $this->finder->find( «որոնման տող»); /* Արդյունքներով ինչ-որ բան արեք */ ))

Այս մոտեցումը (երբ Ծառայությունների կոնտեյների հասանելիությունը չի տրամադրվում հաճախորդների դասերին) կոչվում է կախվածության ներարկում: Բայց այս ձևանմուշն ունի և՛ առավելություններ, և՛ թերություններ: Քանի դեռ մենք հավատարիմ ենք միասնական պատասխանատվության սկզբունքին, կոդը շատ գեղեցիկ տեսք ունի։ Առաջին հերթին մենք ազատվեցինք կոնտեյներից հաճախորդների դասերում՝ դարձնելով դրանց կոդը շատ ավելի պարզ և պարզ: Մենք հեշտությամբ կարող ենք ստուգել կարգավորիչը՝ փոխարինելով անհրաժեշտ կախվածությունները: Մենք կարող ենք ստեղծել և փորձարկել յուրաքանչյուր դաս՝ անկախ մյուսներից (ներառյալ վերահսկիչի դասերը)՝ օգտագործելով TDD կամ BDD մոտեցումը: Թեստեր ստեղծելիս մենք կարող ենք աբստրակտ հեռացնել կոնտեյներից, իսկ ավելի ուշ ավելացնել սահմանում, երբ մեզ անհրաժեշտ է օգտագործել հատուկ օրինակներ: Այս ամենը մեր կոդը կդարձնի ավելի պարզ և հստակ, իսկ թեստավորումը՝ ավելի թափանցիկ:
Բայց հարկ է նշել մեդալի մյուս կողմը. Փաստն այն է, որ կարգավորիչները շատ կոնկրետ դասեր են: Սկսենք նրանից, որ վերահսկիչը, որպես կանոն, պարունակում է գործողությունների մի շարք, ինչը նշանակում է, որ այն խախտում է միասնական պատասխանատվության սկզբունքը։ Արդյունքում, վերահսկիչի դասը կարող է ունենալ շատ ավելի շատ կախվածություն, քան անհրաժեշտ է կոնկրետ գործողություն կատարելու համար: Օգտագործելով ծույլ սկզբնավորումը (օբյեկտը ցուցադրվում է իր առաջին օգտագործման պահին, իսկ մինչ այդ օգտագործվում է թեթև պրոքսի) որոշ չափով լուծում է կատարողականի խնդիրը: Բայց ճարտարապետական ​​տեսանկյունից վերահսկիչից բազմաթիվ կախվածություններ ստեղծելը նույնպես լիովին ճիշտ չէ: Բացի այդ, կարգավորիչների փորձարկումը սովորաբար անհարկի գործողություն է: Ամեն ինչ, իհարկե, կախված է նրանից, թե ինչպես է կազմակերպվում թեստավորումը ձեր դիմումում և ինչպես եք դուք զգում դրա մասին:
Նախորդ պարբերությունից հասկացաք, որ Dependency Injection-ի օգտագործումը ամբողջությամբ չի վերացնում ճարտարապետական ​​խնդիրները: Հետևաբար, մտածեք, թե ինչպես ձեզ համար ավելի հարմար կլինի՝ կոնտեյների հղումը կարգավորիչներում պահել, թե ոչ։ Այստեղ չկա մեկ ճիշտ լուծում: Կարծում եմ, երկու մոտեցումներն էլ լավն են, քանի դեռ վերահսկիչի կոդը մնում է պարզ: Բայց, միանշանակ, կարգավորիչներից բացի չպետք է ստեղծեք Conatiner Aware ծառայություններ:

եզրակացություններ

Դե, եկել է ժամանակը ամփոփելու այն, ինչ ասվել է։ Եվ շատ բան է ասվել... :)
Այսպիսով, օբյեկտների ստեղծման աշխատանքը կառուցելու համար մենք կարող ենք օգտագործել հետևյալ նախշերը.
  • ռեեստրԿաղապարն ունի ակնհայտ թերություններ, որոնցից ամենահիմնականը օբյեկտներ ստեղծելու անհրաժեշտությունն է՝ դրանք ընդհանուր տարայի մեջ դնելուց առաջ: Ակնհայտ է, որ դրանից օգտվելուց մենք ավելի շատ խնդիրներ կունենանք, քան օգուտներ: Սա ակնհայտորեն կաղապարի լավագույն օգտագործումը չէ:
  • Գործարանային մեթոդՆախշի հիմնական առավելությունը. օբյեկտները ստեղծվում են հստակ: Հիմնական թերությունը. վերահսկիչները կա՛մ պետք է անհանգստանան գործարաններ ստեղծելու համար, ինչը ամբողջությամբ չի լուծում դասերի անունների կոշտ կոդավորման խնդիրը, կա՛մ շրջանակը պետք է պատասխանատու լինի վերահսկիչներին բոլոր անհրաժեշտ գործարաններով ապահովելու համար, ինչը այնքան էլ ակնհայտ չի լինի: Օբյեկտների ստեղծման գործընթացը կենտրոնացված կառավարելու հնարավորություն չկա։
  • Ծառայության տեղորոշիչՕբյեկտների ստեղծումը վերահսկելու ավելի առաջադեմ միջոց: Աբստրակցիայի լրացուցիչ մակարդակը կարող է օգտագործվել օբյեկտների ստեղծման ժամանակ հանդիպող ընդհանուր առաջադրանքների ավտոմատացման համար: Օրինակ:
    դասի ServiceContainer-ը ընդլայնում է ArrayObject ( հանրային գործառույթը get($key) ( if (is_callable ($this[$key])) ($obj = call_user_func($this[$key]); if ($obj instanceof RequestAwareInterface) ( $obj- >setRequest($this->get("խնդրանք")); ) վերադարձնել $obj;) նետել նոր \RuntimeException("Չի հաջողվում գտնել ծառայության սահմանումը [ $key ] ստեղնի տակ"); ) )
    Service Locator-ի թերությունն այն է, որ դասերի հանրային API-ն դադարում է լինել տեղեկատվական: Անհրաժեշտ է կարդալ դասի ամբողջ կոդը՝ հասկանալու համար, թե ինչ ծառայություններ են օգտագործվում դրանում։ Այն դասը, որը պարունակում է կոնտեյների հղում, ավելի դժվար է փորձարկել:
  • Կախվածության ներարկումԸստ էության, մենք կարող ենք օգտագործել նույն սպասարկման բեռնարկղը, ինչ նախորդ օրինակի համար: Տարբերությունն այն է, թե ինչպես է օգտագործվում այս տարան: Եթե ​​խուսափենք դասերը կոնտեյներից կախված դարձնելուց, մենք կստանանք հստակ և հստակ դասի API:
Սա այն ամենը չէ, ինչ ես կցանկանայի ձեզ պատմել PHP հավելվածներում օբյեկտներ ստեղծելու խնդրի մասին: Կա նաև Prototype օրինակը, մենք չենք դիտարկել Reflection API-ի օգտագործումը, մի կողմ ենք թողել ծառայությունների ծույլ բեռնման խնդիրը և շատ այլ նրբերանգներ։ Հոդվածը բավականին երկար ստացվեց, այնպես որ ես կավարտեմ այն ​​:)
Ես ուզում էի ցույց տալ, որ կախվածության ներարկումը և այլ օրինաչափություններ այնքան էլ բարդ չեն, որքան սովորաբար ենթադրվում է:
Եթե ​​մենք խոսում ենք Dependency Injection-ի մասին, ապա կան, օրինակ, այս օրինաչափության KISS իրականացումները

Անդրադառնալով ապագա տվյալների բազայի կառուցվածքին. Սկսվել է, և մենք չենք կարող նահանջել, և ես դրա մասին չեմ էլ մտածում։

Մենք մի փոքր ուշ կվերադառնանք տվյալների բազա, բայց առայժմ կսկսենք գրել մեր շարժիչի կոդը։ Բայց նախ, մի քիչ սարքավորում: Սկսել.

Ժամանակի Սկիզբ

Այս պահին մենք ունենք միայն որոշակի պատկերացումներ և պատկերացումներ համակարգի գործունեության մասին, որոնք ցանկանում ենք իրականացնել, բայց ինքնին իրականացում դեռ չկա։ Մենք ոչինչ չունենք աշխատելու. մենք ոչ մի ֆունկցիոնալություն չունենք, և, ինչպես հիշում եք, այն բաժանեցինք 2 մասի` ներքին և արտաքին: Այբուբենը պահանջում է տառեր, բայց արտաքին ֆունկցիոնալությունը պահանջում է ներքին ֆունկցիոնալություն. մենք այստեղից կսկսենք:

Բայց ոչ այնքան արագ: Որպեսզի այն աշխատի, պետք է մի փոքր խորանալ: Մեր համակարգը ներկայացնում է հիերարխիա, և ցանկացած հիերարխիկ համակարգ ունի սկիզբ՝ մոնտաժային կետ Linux-ում, տեղական սկավառակ Windows-ում, պետության, ընկերության, ուսումնական հաստատության համակարգ և այլն: Նման համակարգի յուրաքանչյուր տարր ենթակա է ինչ-որ մեկին և կարող է ունենալ մի քանի ենթականեր, իսկ իր հարևաններին և նրանց ենթականերին դիմելու համար օգտագործում է վերադասներին կամ հենց սկզբին: Հիերարխիկ համակարգի լավ օրինակ է տոհմածառը. ընտրվում է ելակետ՝ ինչ-որ նախահայր, և մենք հեռանում ենք: Մեր համակարգում մեզ անհրաժեշտ է նաև ելակետ, որտեղից մենք կաճենք մասնաճյուղեր՝ մոդուլներ, պլագիններ և այլն։ Մեզ պետք է ինչ-որ ինտերֆեյս, որի միջոցով մեր բոլոր մոդուլները «կհաղորդակցվեն»: Հետագա աշխատանքի համար մենք պետք է ծանոթանանք հայեցակարգին. դիզայնի օրինակ» և դրանց մի քանի իրականացում:

Դիզայնի նախշեր

Բազմաթիվ հոդվածներ կան այն մասին, թե ինչ է դա և ինչ տեսակներ կան, թեման բավականին խորամանկ է, և ես ձեզ նոր բան չեմ ասի: Իմ սիրելի Վիքիում այս թեմայի վերաբերյալ տեղեկատվություն կա՝ սլայդով կառք և մի փոքր ավելին։

Դիզայնի նախշերը նաև հաճախ անվանում են դիզայնի նախշեր կամ պարզապես նախշեր (անգլերեն pattern բառից, թարգմանաբար նշանակում է «նախշ»): Հետագայում հոդվածներում, երբ ես խոսում եմ նախշերի մասին, նկատի կունենամ դիզայնի նախշերը:

Բոլոր տեսակի սարսափելի (և ոչ այնքան սարսափելի) օրինաչափությունների անունների հսկայական ցուցակից մեզ առայժմ միայն երկուսն են հետաքրքրում՝ ռեեստր և սինգլտոն:

ռեեստր (կամ գրանցվեք) օրինաչափություն է, որը գործում է որոշակի զանգվածի վրա, որի մեջ կարող եք ավելացնել և հեռացնել օբյեկտների որոշակի հավաքածու և մուտք ունենալ դրանցից որևէ մեկին և դրա հնարավորություններին:

Միայնակ (կամ միայնակ) օրինաչափություն է, որն ապահովում է դասի միայն մեկ օրինակի առկայությունը: Այն չի կարող պատճենվել, քնեցնել կամ արթնանալ (խոսում է PHP մոգության մասին՝ __clone(), __sleep(), __wakeup()): Singleton-ն ունի գլոբալ մուտքի կետ:

Սահմանումները ամբողջական կամ ընդհանրացված չեն, բայց սա բավական է հասկանալու համար։ Առանձին-առանձին դրանք մեզ ամեն դեպքում պետք չեն։ Մեզ հետաքրքրում է այս օրինաչափություններից յուրաքանչյուրի հնարավորությունները, բայց մեկ դասում. այդպիսի օրինակ կոչվում է singleton ռեեստր կամ Singleton ռեեստր:

Ի՞նչ կտա սա մեզ:
  • Մեզ երաշխավորված կլինի ռեգիստրի մեկ օրինակ, որտեղ մենք կարող ենք ցանկացած պահի ավելացնել օբյեկտներ և օգտագործել դրանք կոդի ցանկացած կետից;
  • անհնար կլինի պատճենել այն և օգտագործել PHP լեզվի այլ անցանկալի (այս դեպքում) կախարդանք:

Այս փուլում բավական է հասկանալ, որ մեկ ռեեստրը մեզ թույլ կտա իրականացնել համակարգի մոդուլային կառուցվածքը, ինչը մենք ցանկանում էինք, երբ քննարկում էինք նպատակները, իսկ մնացածը դուք կհասկանաք, երբ զարգացումն առաջ է գնում:

Դե, բավական բառեր, եկեք ստեղծենք:

Առաջին տողերը

Քանի որ այս դասը կվերաբերի միջուկի ֆունկցիոնալությանը, մենք կսկսենք ստեղծելով մի թղթապանակ մեր նախագծի արմատում, որը կոչվում է core, որտեղ կտեղադրենք միջուկի մոդուլների բոլոր դասերը: Մենք սկսում ենք ռեեստրից, ուստի եկեք անվանենք ֆայլը registry.php

Մեզ չի հետաքրքրում այն ​​հնարավորությունը, որ հետաքրքրասեր օգտատերը մեր ֆայլի ուղղակի հասցե մուտքագրի բրաուզերի տողում, ուստի մենք պետք է պաշտպանվենք դրանից: Այս նպատակին հասնելու համար պարզապես անհրաժեշտ է հիմնական գործարկվող ֆայլում որոշակի հաստատուն սահմանել, որը մենք կստուգենք։ Գաղափարը նոր չէ, որքան հիշում եմ, այն օգտագործվել է Joomla-ում։ Սա պարզ և աշխատանքային մեթոդ է, այնպես որ մենք այստեղ կարող ենք անել առանց հեծանիվների:

Քանի որ մենք պաշտպանում ենք միացված մի բան, մենք կկոչենք հաստատուն _PLUGSECURE_.

Եթե ​​(!defined("_PLUGSECURE_")) ( die("Մոդուլի ուղղակի զանգն արգելված է!"); )

Այժմ, եթե փորձեք ուղղակիորեն մուտք գործել այս ֆայլը, ոչ մի օգտակար բան դուրս չի գա, ինչը նշանակում է, որ նպատակը հասել է:

Հաջորդը, ես առաջարկում եմ մեր բոլոր մոդուլների համար սահմանել որոշակի ստանդարտ: Ես ուզում եմ յուրաքանչյուր մոդուլին տրամադրել գործառույթ, որը կվերադարձնի դրա մասին որոշ տեղեկություններ, օրինակ՝ մոդուլի անվանումը, և այս ֆունկցիան պետք է պահանջվի դասարանում։ Այս նպատակին հասնելու համար մենք գրում ենք հետևյալը.

Ինտերֆեյս StorableObject (հանրային ստատիկ ֆունկցիա getClassName();)

Սրա նման. Հիմա, եթե միացնենք որևէ դաս առանց ֆունկցիայի getClassName()մենք կտեսնենք սխալի հաղորդագրություն: Առայժմ չեմ կենտրոնանա սրա վրա, դա մեզ ավելի ուշ օգտակար կլինի, գոնե թեստավորման և վրիպազերծման համար:

Ժամանակն է, որ դասի մեր միայնակ ռեեստրի ինքնին. Մենք կսկսենք հայտարարելով դասը և դրա որոշ փոփոխականները.

Class Registry-ն իրականացնում է StorableObject ( //մոդուլի անվանումը, ընթեռնելի մասնավոր ստատիկ $className = "Registry"; //registry instance private static $instance; //objects array private static $objects = array();

Առայժմ ամեն ինչ տրամաբանական է և հասկանալի։ Այժմ, ինչպես հիշում եք, մենք ունենք ռեեստր՝ singleton հատկություններով, ուստի եկեք անմիջապես գրենք մի ֆունկցիա, որը թույլ կտա մեզ աշխատել ռեեստրի հետ այս կերպ.

Հանրային ստատիկ ֆունկցիա singleton() ( if(!isset(self::$instance)) ( $obj = __CLASS__; self::$instance = նոր $obj; ) վերադարձնել ինքնությունը::$instance; )

Բառացիորեն. ֆունկցիան ստուգում է, թե արդյոք գոյություն ունի մեր ռեգիստրի օրինակ, եթե ոչ, այն ստեղծում և վերադարձնում է այն, եթե այն արդեն կա, պարզապես վերադարձնում է այն: Այս դեպքում մեզ ինչ-որ կախարդանք պետք չէ, ուստի պաշտպանության համար մենք այն կհայտարարենք որպես մասնավոր.

Մասնավոր ֆունկցիա __construct()() մասնավոր ֆունկցիա __clone()() մասնավոր ֆունկցիա __wakeup()() մասնավոր ֆունկցիա __sleep() ()

Այժմ մեզ անհրաժեշտ է ֆունկցիա մեր ռեեստրում օբյեկտ ավելացնելու համար. այս ֆունկցիան կոչվում է setter, և ես որոշեցի այն իրականացնել երկու եղանակով՝ ցույց տալու, թե ինչպես կարող ենք օգտագործել մոգությունը և տրամադրել օբյեկտ ավելացնելու այլընտրանքային եղանակ: Առաջին մեթոդը ստանդարտ ֆունկցիա է, երկրորդը կատարում է առաջինը __set() մոգության միջոցով:

//$object - ուղի դեպի միացված օբյեկտ //$key - մուտքի բանալի դեպի օբյեկտ ռեգիստրի հանրային ֆունկցիայի մեջ addObject($key, $object) ( require_once($object); //ստեղծել օբյեկտ օբյեկտների զանգվածում: self::$objects[ $key] = new $key(self::$instance); ) //այլընտրանքային մեթոդ կախարդական հանրային ֆունկցիայի միջոցով __set($key, $object) ( $this->addObject($key, $ օբյեկտ);)

Այժմ, մեր ռեեստրում օբյեկտ ավելացնելու համար մենք կարող ենք օգտագործել երկու տեսակի մուտքեր (ասենք, որ մենք արդեն ստեղծել ենք ռեեստրի օրինակ $registry և ցանկանում ենք ավելացնել config.php ֆայլը).

$registry->addObject ("config", "/core/config.php"); //կանոնավոր մեթոդ $registry->config = "/core/config.php"; //PHP կախարդական ֆունկցիայի միջոցով __set()

Երկու մուտքերն էլ կկատարեն նույն գործառույթը. նրանք կմիացնեն ֆայլը, կստեղծեն դասի օրինակ և կտեղադրեն այն ռեգիստրում բանալիով: Այստեղ կա մի կարևոր կետ, որի մասին չպետք է մոռանալ ապագայում. ռեգիստրում օբյեկտի բանալին պետք է համապատասխանի միացված օբյեկտի դասի անվանը: Եթե ​​նորից նայեք կոդը, կհասկանաք, թե ինչու։

Որ ձայնագրությունն օգտագործել՝ կախված է ձեզանից: Ես նախընտրում եմ ձայնագրել կախարդական մեթոդով. այն ավելի «գեղեցիկ» է և կարճ:

Այսպիսով, մենք դասավորել ենք օբյեկտ ավելացնելով, այժմ մեզ անհրաժեշտ է մի ֆունկցիա՝ ստեղնով միացված օբյեկտ մուտք գործելու համար՝ ստացող: Ես նաև իրականացրել եմ այն ​​երկու գործառույթով, որը նման է սեթերին.

//ստացեք օբյեկտը ռեգիստրից //$key - զանգվածի հանրային ֆունկցիայի բանալին getObject($key) ( //ստուգեք՝ արդյոք փոփոխականը օբյեկտ է, եթե (is_object(self::$objects[$key])) ( //եթե այդպես է, ապա մենք վերադարձնում ենք այս օբյեկտի վերադարձը ինքն իրեն::$objects[$key];) ) //նման մեթոդ magic public ֆունկցիայի միջոցով __get($key) ( if (is_object(self::$objects[$) բանալի])) (վերադարձնել ինքն իրեն՝ :$objects[$key]; ) )

Ինչպես սեթերի դեպքում, օբյեկտին հասանելիություն ստանալու համար մենք կունենանք 2 համարժեք գրառում.

$registry->getObject("config"); //կանոնավոր մեթոդ $registry->config; //PHP կախարդական ֆունկցիայի միջոցով __get()

Ուշադիր ընթերցողն անմիջապես կհարցնի. ինչո՞ւ __set() կախարդական ֆունկցիայում ես պարզապես կանչում եմ սովորական (ոչ կախարդական) օբյեկտ ավելացնելու ֆունկցիա, բայց __get() ստացողի մեջ ես նույն կանչի փոխարեն պատճենում եմ getObject() ֆունկցիայի կոդը:Անկեղծ ասած, ես չեմ կարող բավական ճշգրիտ պատասխանել այս հարցին, միայն կասեմ, որ խնդիրներ ունեի այլ մոդուլներում __get() մոգության հետ աշխատելիս, բայց ծածկագիրը «գլխով» վերաշարադրելիս նման խնդիրներ չկան:

Միգուցե դա է պատճառը, որ ես հաճախ եմ հոդվածներում տեսնում կշտամբանքներ PHP-ի կախարդական մեթոդների նկատմամբ և խորհուրդներ՝ խուսափելու դրանցից:

«Ամեն կախարդանք իր գինն ունի»։ © Rumplestiltskin

Այս փուլում մեր ռեեստրի հիմնական ֆունկցիոնալությունն արդեն պատրաստ է. մենք կարող ենք ստեղծել ռեեստրի մեկ օրինակ, ավելացնել օբյեկտներ և մուտք գործել դրանց ինչպես սովորական մեթոդների, այնպես էլ PHP լեզվի կախարդական մեթոդների միջոցով: «Ի՞նչ կասեք ջնջման մասին»:— Մեզ այս ֆունկցիան առայժմ պետք չի լինի, և ես վստահ չեմ, որ ապագայում որևէ բան կփոխվի։ Ի վերջո, մենք միշտ կարող ենք ավելացնել անհրաժեշտ ֆունկցիոնալությունը: Բայց եթե մենք հիմա փորձենք ստեղծել մեր ռեգիստրի օրինակ,

$registry = Registry::singleton();

մենք կստանանք սխալ.

Ճակատագրական սխալԴասի ռեգիստրը պարունակում է 1 վերացական մեթոդ և, հետևաբար, պետք է հայտարարվի վերացական կամ իրականացնի մնացած մեթոդները (StorableObject::getClassName) ...

Բոլորը, քանի որ մենք մոռացել ենք գրել պահանջվող գործառույթը: Հիշու՞մ եք հենց սկզբում ես խոսեցի մի ֆունկցիայի մասին, որը վերադարձնում է մոդուլի անունը: Սա այն է, ինչ մնում է ավելացնել լիարժեք ֆունկցիոնալության համար: Դա պարզ է.

Հանրային ստատիկ ֆունկցիա getClassName() ( վերադարձնում է ինքնությունը::$className; )

Հիմա սխալներ չպետք է լինեն։ Առաջարկում եմ ավելացնել ևս մեկ գործառույթ, այն պարտադիր չէ, բայց վաղ թե ուշ այն կարող է օգտակար լինել, մենք հետագայում այն ​​կօգտագործենք ստուգման և վրիպազերծման համար: Ֆունկցիան կվերադարձնի մեր ռեեստրում ավելացված բոլոր օբյեկտների (մոդուլների) անունները.

Հանրային ֆունկցիան getObjectsList() ( //զանգվածը, որը մենք կվերադարձնենք $names = array(); //ստացեք յուրաքանչյուր օբյեկտի անունը foreach օբյեկտների զանգվածից (self::$objects որպես $obj) ( $names = $ obj->getClassName() ;) //ավելացնել ռեգիստրի մոդուլի անունը զանգվածի_push($names, self::getClassName()); //և վերադարձնել $names;)

Այսքանը: Սա լրացնում է գրանցամատյանը: Եկեք ստուգենք նրա աշխատանքը: Ստուգելիս մեզ անհրաժեշտ կլինի ինչ-որ բան միացնել՝ թող լինի կազմաձևման ֆայլ: Ստեղծեք նոր core/config.php ֆայլ և ավելացրեք այն նվազագույն բովանդակությունը, որը պահանջում է մեր ռեեստրը.

//մի մոռացեք ստուգել հաստատունը if (!defined("_PLUGSECURE_")) ( die("Մոդուլի ուղղակի զանգը արգելված է!"); "; հանրային ստատիկ ֆունկցիա getClassName() ( վերադարձնում է ինքնությունը::$className; ) )

Նման մի բան. Հիմա եկեք անցնենք ինքնին ստուգմանը: Մեր նախագծի արմատում ստեղծեք index.php ֆայլ և դրա մեջ գրեք հետևյալ կոդը.

Սահմանել ("_PLUGSECURE_", true); //սահմանել է հաստատուն՝ օբյեկտներին ուղղակի մուտքից պաշտպանելու համար require_once «/core/registry.php»; //միացրել է ռեգիստրը $registry = Registry::singleton(); //ստեղծեց ռեգիստր singleton օրինակ $registry->config = "/core/config.php"; //միացնել մեր, առայժմ անօգուտ, կոնֆիգուրգը //ցուցադրել միացված մոդուլների անունները echo " Միացված է"; foreach ($registry->

  • " . $names ."
  • "; }

    Կամ, եթե դուք դեռ խուսափում եք կախարդությունից, ապա 5-րդ տողը կարող է փոխարինվել այլընտրանքային մեթոդով.

    Սահմանել ("_PLUGSECURE_", true); //սահմանել է հաստատուն՝ օբյեկտներին ուղղակի մուտքից պաշտպանելու համար require_once «/core/registry.php»; //միացրել է ռեգիստրը $registry = Registry::singleton(); //ստեղծել է ռեգիստրի միայնակ օրինակ $registry->addObject("config", "/core/config.php"); //միացնել մեր, առայժմ անօգուտ, կոնֆիգուրգը //ցուցադրել միացված մոդուլների անունները echo " Միացված է"; foreach ($registry->getObjectsList() որպես $names) (echo "

  • " . $names ."
  • "; }

    Այժմ բացեք զննարկիչը և հասցեագոտում գրեք http://localhost/index.php կամ պարզապես http://localhost/ (համապատասխան է, եթե դուք օգտագործում եք ստանդարտ Open Server կամ նմանատիպ վեբ սերվերի կարգավորումներ)

    Արդյունքում, մենք պետք է տեսնենք նման բան.

    Ինչպես տեսնում եք, սխալներ չկան, ինչը նշանակում է, որ ամեն ինչ աշխատում է, ինչի համար շնորհավորում եմ :)

    Այսօր մենք կանգ կառնենք սրա վրա։ Հաջորդ հոդվածում մենք կվերադառնանք տվյալների շտեմարան և կգրենք MySQL SUDB-ի հետ աշխատելու դաս, միացնենք այն ռեգիստրին և փորձարկենք աշխատանքը գործնականում։ Կտեսնվենք!

    Այս օրինաչափությունը, ինչպես Singleton-ը, հազվադեպ է ծրագրավորողների դրական արձագանքի պատճառ դառնում, քանի որ այն նույն խնդիրներն է առաջացնում հավելվածների փորձարկման ժամանակ: Այնուամենայնիվ, նրանք նախատում են, բայց ակտիվորեն օգտագործում են. Ինչպես Singleton-ը, Registry-ի օրինաչափությունը հանդիպում է բազմաթիվ հավելվածներում և, այսպես թե այնպես, զգալիորեն հեշտացնում է որոշակի խնդիրների լուծումը:

    Դիտարկենք երկու տարբերակներն էլ ըստ հերթականության։

    Այն, ինչ կոչվում է «մաքուր ռեեստր» կամ պարզապես ռեգիստր, ստատիկ ինտերֆեյսով դասի իրականացում է: Singleton օրինակից հիմնական տարբերությունն այն է, որ այն արգելափակում է դասի առնվազն մեկ օրինակ ստեղծելու հնարավորությունը: Սա հաշվի առնելով՝ իմաստ չունի թաքցնել կախարդական մեթոդները՝ __clone() և __wakeup() մասնավոր կամ պաշտպանված փոփոխիչի հետևում:

    Ռեեստրի դասպետք է ունենա երկու ստատիկ մեթոդ՝ ստացող և սեթեր: Սեթերը անցած օբյեկտը տեղադրում է պահեստում՝ կապված տվյալ բանալիով: Ստացողը, համապատասխանաբար, խանութից առարկա է վերադարձնում: Խանութը ոչ այլ ինչ է, քան ասոցիատիվ բանալի-արժեքային զանգված:

    Ռեեստրի ամբողջական վերահսկողության համար ներկայացվում է մեկ այլ ինտերֆեյսի տարր՝ մեթոդ, որը թույլ է տալիս ջնջել օբյեկտը պահեստից:

    Ի հավելումն Սինգլթոնի օրինակին նույնական խնդիրների, կան ևս երկուսը.

    • ներմուծել մեկ այլ տեսակի կախվածություն՝ ռեեստրի բանալիներից.
    • երկու տարբեր ռեեստրի բանալիներ կարող են հղում ունենալ նույն օբյեկտին

    Առաջին դեպքում հնարավոր չէ խուսափել լրացուցիչ կախվածությունից։ Որոշ չափով մենք կապվում ենք հիմնական անունների հետ:

    Երկրորդ խնդիրը լուծվում է Registry::set() մեթոդի մեջ ստուգում ներմուծելով.

    Հանրային ստատիկ ֆունկցիաների հավաքածու ($key, $item) ( if (!array_key_exists($key, self::$_registry)) (foreach (self::$_registry որպես $val) (եթե ($val === $item) ( գցել նոր Բացառություն («Նյութն արդեն գոյություն ունի»); ) ) ինքն իրեն::$_registry[$key] = $item; ) )

    « Մաքրել ռեեստրի օրինակը«առաջացնում է մեկ այլ խնդիր՝ մեծացնելով կախվածությունը դասի անվան միջոցով սեթեր և ստացող մուտք գործելու անհրաժեշտության պատճառով: Դուք չեք կարող հղում ստեղծել օբյեկտին և աշխատել դրա հետ, ինչպես դա եղավ Singleton օրինակի դեպքում, երբ այս մոտեցումը հասանելի էր.

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

    Այստեղ մենք հնարավորություն ունենք պահպանել հղումը Singleton օրինակին, օրինակ, ընթացիկ դասի հատկությունում և աշխատել դրա հետ, ինչպես պահանջում է OOP գաղափարախոսությունը. փոխանցել այն որպես պարամետր ագրեգացված օբյեկտներին կամ օգտագործել այն ժառանգների մեջ:

    Այս հարցը լուծելու համար կա Singleton ռեգիստրի իրականացում, որը շատերին դուր չի գալիս, քանի որ թվում է, թե ավելորդ կոդ է: Կարծում եմ, որ այս վերաբերմունքի պատճառը OOP-ի սկզբունքների որոշակի թյուրիմացությունն է կամ դրանց դիտավորյալ անտեսումը:

    _registry[$key] = $object; ) ստատիկ հանրային ֆունկցիա get($key) ( return self::getInstance()->_registry[$key];) մասնավոր ֆունկցիա __wakeup() () մասնավոր ֆունկցիա __construct() () մասնավոր ֆունկցիա __clone() ( ) ) ?>

    Գումար խնայելու համար ես միտումնավոր բաց թողեցի մեկնաբանությունների բլոկները մեթոդների և հատկությունների համար: Չեմ կարծում, որ դրանք անհրաժեշտ են։

    Ինչպես արդեն ասացի, հիմնարար տարբերությունն այն է, որ այժմ հնարավոր է պահպանել հղումը ռեեստրի ծավալին և ամեն անգամ չօգտագործել ծանրակշիռ զանգեր ստատիկ մեթոդներին: Այս տարբերակն ինձ մի փոքր ավելի ճիշտ է թվում։ Իմ կարծիքի հետ համաձայն լինելը կամ չհամաձայնելը մեծ նշանակություն չունի, ինչպես և իմ կարծիքը: Իրականացման ոչ մի նրբություն չի կարող վերացնել օրինաչափությունը նշված մի շարք թերություններից։

    Որոշեցի հակիրճ գրել մեր կյանքում հաճախ օգտագործվող օրինաչափությունների մասին, ավելի շատ օրինակներ, քիչ ջուր, գնանք:

    Սինգլթոն

    «Սինգլի» հիմնական կետն այն է, որ երբ ասում եք «ինձ հեռախոսային կայան է պետք», նրանք ձեզ կասեին «Այնտեղ արդեն կառուցված է», և ոչ թե «Եկեք նորից կառուցենք»: «Միայնակ»ը միշտ մենակ է։

    Դաս Singleton ( մասնավոր ստատիկ $instance = null; մասնավոր ֆունկցիա __construct())( /* ... @return Singleton */ ) // Պաշտպանություն ստեղծումից նոր Singleton մասնավոր ֆունկցիայի միջոցով __clone() ( /* ... @return Singleton * / ) // Պաշտպանեք ստեղծումից կլոնավորման մասնավոր ֆունկցիայի միջոցով __wakeup() ( /* ... @return Singleton */ ) // Պաշտպանեք ստեղծումից՝ unserialize հանրային ստատիկ ֆունկցիայի միջոցով getInstance() ( if (is_null(self::$instance) ) ) ( self::$instance = նոր ես; ) վերադարձնել ինքն իրեն::$instance; ) )

    Գրանցամատյան (գրանցամատյան, գրառումների ամսագիր)

    Ինչպես անունն է հուշում, այս օրինաչափությունը նախատեսված է պահելու գրառումները, որոնք տեղադրված են դրանում և, համապատասխանաբար, վերադարձնելու այդ գրառումները (անունով), եթե դրանք պահանջվում են: Հեռախոսակայանի օրինակում դա ռեգիստր է բնակիչների հեռախոսահամարների նկատմամբ։

    Դասի ռեեստր ( մասնավոր $registry = array (); հանրային գործառույթների հավաքածու ($key, $object) ( $this->registry[$key] = $object; ) հանրային ֆունկցիա get($key) ( վերադարձ $this->registry [$ key];))

    Singleton ռեեստր- մի շփոթեք)

    «Ռեգիստրը» հաճախ «միայնակ» է, բայց միշտ չէ, որ պետք է այդպես լինի: Օրինակ, հաշվապահական հաշվառման բաժնում կարող ենք ստեղծել մի քանի ամսագրեր, որոնցից մեկում «Ա»-ից մինչև «Մ» աշխատողներ կան, մյուսում՝ «Ն»-ից «Զ»-ն: Յուրաքանչյուր նման ամսագիր կլինի «ռեգիստր», բայց ոչ «մեկ», քանի որ արդեն կա 2 ամսագիր։

    Դաս SingletonRegistry ( մասնավոր ստատիկ $instance = null; private $registry = array(); մասնավոր ֆունկցիա __construct() ( /* ... @return Singleton */ ) // Պաշտպանեք ստեղծումից նոր Singleton մասնավոր ֆունկցիայի միջոցով __clone() (/ * ... @return Singleton */ ) // Պաշտպանեք ստեղծումից՝ կլոնավորման մասնավոր ֆունկցիայի միջոցով __wakeup() ( /* ... @return Singleton */ ) // Պաշտպանեք ստեղծումից՝ unserialize հանրային ստատիկ ֆունկցիայի միջոցով getInstance() ( if ( is_null(self::$instance)) (self::$instance = նոր ինքնորոշում; ) վերադարձ ինքնորոշում::$instance; ) հանրային գործառույթների հավաքածու ($key, $object) ($this->registry[$key] = $ օբյեկտ; ) հանրային ֆունկցիա get($key) ( վերադարձ $this->registry[$key]; ) )

    Multiton («սինգլների» լողավազան) կամ այլ կերպ ասածRegistry Singleton ) - մի շփոթեք Singleton Registry-ի հետ

    Հաճախ «ռեգիստրը» օգտագործվում է հատուկ «սինգլներ» պահելու համար: Բայց, քանի որ «ռեգիստրի» օրինաչափությունը «գեներատիվ օրինաչափություն» չէ, բայց ես կցանկանայի դիտարկել «գրանցումը»՝ կապված «զինգլտոնի» հետ:Դրա համար մենք օրինաչափություն ստեղծեցինք Մուլտիտոն, որը ըստԻր հիմքում այն ​​«ռեգիստր» է, որը պարունակում է մի քանի «սինգլներ», որոնցից յուրաքանչյուրն ունի իր «անունը», որով կարելի է մուտք գործել:

    Կարճ: թույլ է տալիս ստեղծել այս դասի օբյեկտներ, բայց միայն այն դեպքում, եթե դուք անվանեք օբյեկտը: Իրական կյանքի օրինակ չկա, բայց ես համացանցում գտա հետևյալ օրինակը.

    Դասի տվյալների շտեմարան ( մասնավոր ստատիկ $instances = array(); մասնավոր ֆունկցիա __construct() () մասնավոր ֆունկցիա __clone() () հանրային ստատիկ ֆունկցիա getInstance($key) (if(!array_key_exists($key, self::$instances)) ( self::$instances[$key] = new self(); ) return self::$instances[$key]; ) ) $master = Տվյալների բազա::getInstance("master"); var_dump ($ վարպետ); // օբյեկտ (Տվյալների բազա)#1 (0) ( ) $logger = Տվյալների բազա::getInstance("logger"); var_dump ($logger); // օբյեկտ (Տվյալների բազա)#2 (0) ( ) $masterDupe = Տվյալների շտեմարան::getInstance("master"); var_dump ($masterDupe); // օբյեկտ (Տվյալների շտեմարան)#1 (0) ( ) // Ճակատագրական սխալ. Կանչ մասնավոր տվյալների շտեմարան.:__construct() անվավեր համատեքստից $dbFatalError = new Database(); // PHP Ճակատագրական սխալ. Զանգել մասնավոր տվյալների շտեմարան::__clone() $dbCloneError = կլոն $masterDupe;

    Օբյեկտների լողավազան

    Ըստ էության, այս օրինակն է «ռեգիստր», որը պահում է միայն առարկաներ, առանց տողերի, զանգվածների և այլն: տվյալների տեսակները.

    Գործարան

    Կաղապարի էությունը գրեթե ամբողջությամբ նկարագրված է իր անունով. Երբ անհրաժեշտ է ձեռք բերել որոշ առարկաներ, օրինակ՝ հյութերի տուփեր, պետք չէ իմանալ, թե ինչպես են դրանք արտադրվում գործարանում: Դուք պարզապես ասում եք՝ «տվեք ինձ մի տուփ նարնջի հյութ», և «գործարանը» ձեզ վերադարձնում է անհրաժեշտ փաթեթը: Ինչպե՞ս: Այս ամենը որոշում է հենց գործարանը, օրինակ՝ «պատճենում» է արդեն գոյություն ունեցող ստանդարտը։ «Գործարանի» հիմնական նպատակն է հնարավորության դեպքում, անհրաժեշտության դեպքում, փոխել հյութի փաթեթի «արտաքին տեսքի» գործընթացը, և ինքը՝ սպառողին, պետք չէ այդ մասին որևէ բան ասել, որպեսզի նա կարողանա դա պահանջել։ ինչպես նախկինում: Որպես կանոն, մեկ գործարանը զբաղվում է միայն մեկ տեսակի «արտադրանքի» «արտադրմամբ»։ Խորհուրդ չի տրվում «հյութերի գործարան» ստեղծել՝ հաշվի առնելով մեքենաների անվադողերի արտադրությունը։ Ինչպես կյանքում, գործարանային օրինակը հաճախ ստեղծվում է միայնակ մարդու կողմից:

    Աբստրակտ դաս AnimalAbstract ( պաշտպանված $species; հանրային ֆունկցիա getSpecies() ( return $this->species; ) ) class Cat extensions AnimalAbstract ( protected $species = "cat"; ) class Dog extends AnimalAbstract ( protected $species = "dog"; ) դաս AnimalFactory ( հանրային ստատիկ ֆունկցիայի գործարան ($կենդանի) ( անջատիչ ($կենդանի) («cat»՝ $obj = new Cat(); break; case «dog»: $obj = new Dog(); break; default նետել նոր Բացառություն ("Կենդանիների գործարանը չի կարող ստեղծել տեսակի կենդանիներ "" . $animal . """, 1000); ) վերադարձ $obj; ) ) $cat = AnimalFactory::factory("cat"); // օբյեկտ (Cat)#1 echo $cat->getSpecies(); // cat $dog = AnimalFactory::factory("dog"); // օբյեկտ (Շուն)#1 echo $dog->getSpecies(); // շուն $hippo = AnimalFactory::factory("hippopotamus"); // Սա բացառություն կբերի

    Ուզում եմ ձեր ուշադրությունը հրավիրել այն փաստի վրա, որ գործարանային մեթոդը նույնպես օրինաչափություն է, այն կոչվում է Գործարանային մեթոդ։

    Շինարար (շինարար)

    Այսպիսով, մենք արդեն հասկացել ենք, որ «Factory»-ն ըմպելիքների վաճառքի մեքենա է, այն արդեն ունի ամեն ինչ պատրաստ է, իսկ դուք պարզապես ասեք այն, ինչ ձեզ հարկավոր է։ «Builder»-ը այս ըմպելիքներն արտադրող գործարան է և պարունակում է բոլոր բարդ գործողությունները և կարող է հավաքել բարդ առարկաներ ավելի պարզներից (փաթեթավորում, պիտակ, ջուր, համային տեսականի և այլն)՝ կախված պահանջից:

    Class Bottle ( public $name; public $liters; ) /** * բոլոր կառուցողները պետք է */ ինտերֆեյս BottleBuilderInterface ( public function setName(); public function setLiters(); public function getResult(); ) class CocaColaBuilder-ը իրականացնում է BottleBuilderInterface ( մասնավոր $ շիշ; հանրային ֆունկցիա __construct() ($this->bottle = new Bottle(); ) հանրային ֆունկցիա setName($value) ($this->bottle->name = $value; ) հանրային ֆունկցիա setLiters($value) ($ this->bottle->liters = $value; ) public ֆունկցիա getResult() ( return $this->bottle; ) ) $juice = new CocaColaBuilder(); $juice->setName("Coca-Cola Light"); $juice->setLiters(2); $juice->getResult();

    Նախատիպ

    Գործարան հիշեցնելով՝ այն նաև ծառայում է օբյեկտներ ստեղծելու համար, բայց մի փոքր այլ մոտեցմամբ։ Պատկերացրու քեզ բարում, գարեջուր էիր խմում ու վերջանում է, բարմենին ասում ես՝ ինձ էլի նույն տեսակի սարքիր։ Բարմենն իր հերթին նայում է ձեր խմած գարեջրին և կրկնօրինակում է ձեր խնդրանքը: PHP-ն արդեն ունի այս օրինաչափության իրականացումը, այն կոչվում է.

    $newJuice = կլոն $juice;

    Ծույլ սկզբնավորում

    Օրինակ, ղեկավարը տեսնում է տարբեր տեսակի գործունեության հաշվետվությունների ցանկը և կարծում է, որ այդ հաշվետվություններն արդեն գոյություն ունեն, բայց իրականում ցուցադրվում են միայն հաշվետվությունների անունները, իսկ հաշվետվություններն իրենք դեռ չեն ստեղծվել և միայն կստեղծվեն։ պատվերի դեպքում (օրինակ՝ սեղմելով Դիտել հաշվետվություն կոճակը): Ծույլ սկզբնավորման հատուկ դեպքը օբյեկտի ստեղծումն է այն պահին, երբ այն հասանելի է:Վիքիպեդիայում կարող եք գտնել հետաքրքիր մեկը, բայց... Ըստ տեսության՝ php-ում ճիշտ օրինակը կլինի, օրինակ, ֆունկցիան

    Ադապտոր կամ փաթաթան (ադապտեր, փաթաթան)

    Այս նախշը լիովին համապատասխանում է իր անվանը։ «Սովետական» վարդակից եվրո վարդակից աշխատելու համար անհրաժեշտ է ադապտեր: Սա հենց այն է, ինչ անում է «ադապտեր»-ը. այն ծառայում է որպես միջանկյալ օբյեկտ երկու մյուսների միջև, որոնք չեն կարող ուղղակիորեն աշխատել միմյանց հետ: Չնայած սահմանմանը, գործնականում ես դեռ տեսնում եմ Adapter-ի և Wrapper-ի տարբերությունը:

    Դաս MyClass ( հանրային ֆունկցիա մեթոդԱ() () ) դաս MyClassWrapper ( հանրային ֆունկցիա __construct())( $this->myClass = նոր MyClass(); ) հանրային ֆունկցիա __call($name, $arguments)( Log::info(" Դուք պատրաստվում եք զանգահարել $name մեթոդը։"); վերադարձնել call_user_func_array(array($this->myClass, $name), $arguments); ) ) $obj = new MyClassWrapper(); $obj->methodA();

    Կախվածության ներարկում

    Կախվածության ներարկումը թույլ է տալիս որոշ ֆունկցիոնալության պատասխանատվության մի մասը տեղափոխել այլ օբյեկտներ: Օրինակ, եթե մեզ պետք է նոր կադրեր վարձել, ապա մենք չենք կարող ստեղծել մեր սեփական կադրերի բաժինը, այլ կախվածություն մտցնել հավաքագրող ընկերությունից, որն իր հերթին մեր առաջին խնդրանքով «մեզ մարդ է պետք», կամ կաշխատի որպես Կադրերի բաժինն ինքը, կամ կգտնի մեկ այլ ընկերություն (օգտագործելով «ծառայության տեղորոշիչ»), որը կմատուցի այդ ծառայությունները:
    «Կախվածության ներարկումը» թույլ է տալիս փոխել և փոխանակել ընկերության առանձին մասեր՝ չկորցնելով ընդհանուր ֆունկցիոնալությունը:

    Դաս AppleJuice () // այս մեթոդը կախվածության ներարկման օրինաչափության պարզունակ իրականացումն է, և հետագայում դուք կտեսնեք այս ֆունկցիան getBottleJuice()) ($obj = նոր Խնձորի հյութ Խնձորի հյութ)( վերադարձ $obj; ) ) $bottleJuice = getBottleJuice();

    Հիմա պատկերացրեք, որ մենք այլևս խնձորի հյութ չենք ուզում, մենք նարնջի հյութ ենք ուզում։

    Դաս AppleJuice() Դաս Նարնջի հյութ() // այս մեթոդն իրականացնում է կախվածության ներարկման ֆունկցիան getBottleJuice())( $obj = նոր Նարնջի հյութ; // ստուգեք օբյեկտը, եթե նրանք մեզ սայթաքեն գարեջուր (գարեջուրը հյութ չէ) if($obj instanceof Նարնջի հյութ)( վերադարձ $obj; ) )

    Ինչպես տեսնում եք, մենք ստիպված էինք փոխել ոչ միայն հյութի տեսակը, այլև հյութի տեսակի ստուգումը, որն այնքան էլ հարմար չէ։ Շատ ավելի ճիշտ է օգտագործել Կախվածության հակադարձման սկզբունքը.

    Ինտերֆեյս Juice () Class AppleJuice-ն իրականացնում է Juice () Class OrangeJuice-ն իրականացնում է Juice () ֆունկցիան getBottleJuice())( $obj = նոր OrangeJuice; // ստուգեք օբյեկտը, եթե նրանք մեզ գարեջուր են սայթաքել (գարեջուրը հյութ չէ) if($obj): օրինակ Հյութ)( վերադարձ $obj; ) )

    Կախվածության ինվերսիան երբեմն շփոթում են կախվածության ներարկման հետ, բայց դրանք շփոթելու կարիք չկա, քանի որ. Կախվածության շրջադարձը սկզբունք է, ոչ թե օրինաչափություն:

    Ծառայության տեղորոշիչ

    «Service Locator»-ը «Կախվածության ներարկում» իրականացնելու մեթոդ է: Այն վերադարձնում է տարբեր տեսակի օբյեկտներ՝ կախված սկզբնավորման կոդից: Թող խնդիր լինի շինարարի, գործարանի կամ այլ բանի կողմից ստեղծված մեր հյութերի փաթեթը առաքել, որտեղ գնորդը ցանկանա։ Մենք տեղորոշողին ասում ենք «տո՛ւր մեզ առաքման ծառայություն» և ծառայությանը խնդրում ենք հյութը հասցնել ցանկալի հասցեով: Այսօր կա մի ծառայություն, իսկ վաղը կարող է լինել մեկ այլ ծառայություն։ Մեզ համար կարևոր չէ, թե կոնկրետ ինչ ծառայություն է դա, մեզ համար կարևոր է իմանալ, որ այս ծառայությունը կտրամադրի այն, ինչ մենք ասում ենք և որտեղ ենք ասում: Իր հերթին ծառայություններն իրականացնում են «Առաքում<предмет>վրա<адрес>».

    Եթե ​​խոսենք իրական կյանքի մասին, ապա, հավանաբար, ծառայության տեղորոշիչի լավ օրինակը կլինի PDO PHP ընդլայնումը, քանի որ Այսօր մենք աշխատում ենք MySQL տվյալների բազայով, իսկ վաղը կարող ենք աշխատել PostgreSQL-ի հետ։ Ինչպես արդեն հասկացաք, մեր դասի համար կարևոր չէ, թե որ տվյալների բազան է ուղարկում իր տվյալները, կարևոր է, որ նա կարող է դա անել:

    $db = նոր PDO (" mysql:dbname=test;host=localhost", $user, $pass); $db = նոր PDO (" pgsql:dbname=test host=localhost», $user, $pass);

    Կախվածության ներարկման և ծառայության տեղորոշիչի միջև տարբերությունը

    Եթե ​​դեռ չեք նկատել, կուզենայի բացատրել. Կախվածության ներարկումԱրդյունքում, այն վերադարձնում է ոչ թե ծառայություն (որը կարող է ինչ-որ տեղ ինչ-որ բան մատուցել), այլ օբյեկտ, որի տվյալները օգտագործում է։

    Ես կփորձեմ ձեզ պատմել PHP-ում Registry օրինակի իմ ներդրման մասին: Ռեեստրը գլոբալ փոփոխականների OOP-ի փոխարինումն է, որը նախատեսված է տվյալների պահպանման և համակարգի մոդուլների միջև փոխանցելու համար: Ըստ այդմ, այն օժտված է ստանդարտ հատկություններով՝ գրել, կարդալ, ջնջել։ Ահա տիպիկ իրականացում.

    Դե, այս կերպ մենք ստանում ենք մեթոդների հիմար փոխարինում $key = $value - Registry::set($key, $value) $key - Registry::get($key) unset($key) - հեռացնել Registry::remove ($key ) Պարզապես անհասկանալի է. ինչու է այս լրացուցիչ կոդը: Այսպիսով, եկեք սովորեցնենք մեր դասարանին անել այն, ինչ գլոբալ փոփոխականները չեն կարող անել: Վրան պղպեղ ավելացնենք։

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

    Նմուշի բնորոշ առաջադրանքներին ես ավելացրեցի փոփոխականը փոփոխություններից արգելափակելու հնարավորությունը, սա շատ հարմար է մեծ նախագծերի համար, դուք պատահաբար ոչինչ չեք տեղադրի: Օրինակ՝ հարմար է տվյալների բազաների հետ աշխատելու համար
    define('DB_DNS', 'mysql:host=localhost;dbname= ’);
    define('DB_USER', ' ’);
    define('DB_PASSWORD', ' ’);
    define('DB_HANDLE');

    Amdy_Regisrtry::set(DB_HANDLE, նոր PDO(DB_DNS, DB_USER, DB_PASSWORD));
    Amdy_Registry::lock(DB_HANDLE);

    Այժմ կոդի բացատրության համար տվյալների պահպանման համար մենք օգտագործում ենք $data ստատիկ փոփոխականը, $lock փոփոխականը պահում է տվյալներ այն ստեղների մասին, որոնք կողպված են փոփոխության համար: Ցանցում մենք ստուգում ենք՝ արդյոք փոփոխականը կողպված է և փոխում կամ ավելացնում ենք այն ռեգիստրում։ Ջնջելիս մենք նաև ստուգում ենք կողպեքը, ստացողը մնում է անփոփոխ, բացառությամբ լռելյայն ընտրովի պարամետրի: Դե, արժե ուշադրություն դարձնել բացառությունների մշակմանը, որը ինչ-ինչ պատճառներով հազվադեպ է օգտագործվում: Ի դեպ, ես արդեն ունեմ բացառությունների նախագիծ, սպասեք հոդվածին: Ստորև բերված է թեստավորման համար նախատեսված ծածկագրի նախագիծ, ահա թեստավորման մասին հոդված, դա նույնպես վատ չի լինի գրել, չնայած ես TDD-ի երկրպագու չեմ:

    Հաջորդ հոդվածում մենք ավելի կընդլայնենք ֆունկցիոնալությունը՝ ավելացնելով տվյալների սկզբնականացում և իրականացնելով «ծուլություն»: