PHP में OOP अनुप्रयोगों में ऑब्जेक्ट आरंभीकरण की समस्या। रजिस्ट्री, फ़ैक्टरी विधि, सेवा लोकेटर और निर्भरता इंजेक्शन पैटर्न का उपयोग करके समाधान खोजना। उदाहरण और विवरण के साथ ओओपी पैटर्न, आधिकारिक पंजीकरण php

PHP में OOP अनुप्रयोगों में ऑब्जेक्ट आरंभीकरण की समस्या। रजिस्ट्री, फ़ैक्टरी विधि, सेवा लोकेटर और निर्भरता इंजेक्शन पैटर्न का उपयोग करके समाधान खोजना

ऐसा ही होता है कि प्रोग्रामर डिज़ाइन पैटर्न के रूप में सफल समाधानों को समेकित करते हैं। पैटर्न पर बहुत सारा साहित्य है। एरिच गामा, रिचर्ड हेल्म, राल्फ जॉनसन और जॉन व्लिसाइड्स द्वारा द गैंग ऑफ़ फोर की पुस्तक "डिज़ाइन पैटर्न" और, शायद, मार्टिन फाउलर द्वारा "एंटरप्राइज़ एप्लिकेशन आर्किटेक्चर के पैटर्न" को निश्चित रूप से क्लासिक्स माना जाता है। सबसे अच्छी बात जो मैंने उदाहरणों के साथ पढ़ी है PHP में - यह। ऐसा ही होता है कि यह सारा साहित्य उन लोगों के लिए काफी जटिल है, जिन्होंने अभी-अभी OOP में महारत हासिल करना शुरू किया है। इसलिए मेरे मन में कुछ ऐसे पैटर्न प्रस्तुत करने का विचार आया जो मुझे सबसे उपयोगी लगते हैं, उन्हें बहुत ही सरलीकृत रूप में प्रस्तुत किया जाए। अन्य में शब्दों में, यह लेख KISS शैली में डिज़ाइन पैटर्न की व्याख्या करने का मेरा पहला प्रयास है।
आज हम इस बारे में बात करेंगे कि OOP एप्लिकेशन में ऑब्जेक्ट आरंभीकरण के साथ क्या समस्याएं उत्पन्न हो सकती हैं और आप इन समस्याओं को हल करने के लिए कुछ लोकप्रिय डिज़ाइन पैटर्न का उपयोग कैसे कर सकते हैं।

उदाहरण

एक आधुनिक ओओपी एप्लिकेशन दसियों, सैकड़ों और कभी-कभी हजारों वस्तुओं के साथ काम करता है। खैर, आइए इस पर करीब से नज़र डालें कि हमारे अनुप्रयोगों में इन वस्तुओं को कैसे आरंभ किया जाता है। ऑब्जेक्ट इनिशियलाइज़ेशन ही एकमात्र पहलू है जो इस लेख में हमारी रुचि रखता है, इसलिए मैंने सभी "अतिरिक्त" कार्यान्वयन को छोड़ने का निर्णय लिया।
मान लीजिए कि हमने एक सुपर-डुपर उपयोगी वर्ग बनाया है जो एक विशिष्ट यूआरआई को जीईटी अनुरोध भेज सकता है और सर्वर प्रतिक्रिया से HTML लौटा सकता है। ताकि हमारी कक्षा बहुत सरल न लगे, उसे परिणाम की भी जांच करने दें और यदि सर्वर "गलत" प्रतिक्रिया देता है तो एक अपवाद फेंक दें।

क्लास ग्रैबर (सार्वजनिक फ़ंक्शन get($url) (/** HTML कोड लौटाता है या एक अपवाद फेंकता है */))

आइए एक और क्लास बनाएं जिसका ऑब्जेक्ट प्राप्त HTML को फ़िल्टर करने के लिए ज़िम्मेदार होगा। फ़िल्टर विधि HTML कोड और CSS चयनकर्ता को तर्क के रूप में लेती है, और यह दिए गए चयनकर्ता के लिए पाए गए तत्वों की एक सरणी लौटाती है।

क्लास HtmlExtractor (सार्वजनिक फ़ंक्शन फ़िल्टर ($ html, $ चयनकर्ता) (/** फ़िल्टर किए गए तत्वों की सरणी लौटाता है */))

अब, कल्पना करें कि हमें दिए गए कीवर्ड के लिए Google पर खोज परिणाम प्राप्त करने की आवश्यकता है। ऐसा करने के लिए, हम एक और क्लास पेश करेंगे जो अनुरोध भेजने के लिए ग्रैबर क्लास का उपयोग करेगा, और आवश्यक सामग्री निकालने के लिए HtmlExtractor क्लास का उपयोग करेगा। इसमें यूआरआई के निर्माण के लिए तर्क, प्राप्त HTML को फ़िल्टर करने और प्राप्त परिणामों को संसाधित करने के लिए एक चयनकर्ता भी शामिल होगा।

क्लास GoogleFinder (निजी $grabber; निजी $filter; सार्वजनिक फ़ंक्शन __construct() ($this->grabber = new Grabber(); $this->filter = new HtmlExtractor(); ) सार्वजनिक फ़ंक्शन ढूंढें($searchString) ( /* * स्थापित परिणामों की सरणी लौटाता है */) )

क्या आपने देखा कि ग्रैबर और HtmlExtractor ऑब्जेक्ट का आरंभीकरण GoogleFinder क्लास के कंस्ट्रक्टर में है? आइए विचार करें कि यह निर्णय कितना अच्छा है।
बेशक, किसी कंस्ट्रक्टर में ऑब्जेक्ट के निर्माण को हार्डकोड करना एक अच्छा विचार नहीं है। और यही कारण है। सबसे पहले, हम वास्तविक अनुरोध भेजने से बचने के लिए परीक्षण वातावरण में ग्रैबर क्लास को आसानी से ओवरराइड नहीं कर पाएंगे। निष्पक्ष होने के लिए, यह कहने लायक है कि यह रिफ्लेक्शन एपीआई का उपयोग करके किया जा सकता है। वे। तकनीकी संभावना मौजूद है, लेकिन यह सबसे सुविधाजनक और स्पष्ट तरीके से बहुत दूर है।
दूसरे, यदि हम अन्य ग्रैबर और HtmlExtractor कार्यान्वयन के साथ GoogleFinder तर्क का पुन: उपयोग करना चाहते हैं तो भी यही समस्या उत्पन्न होगी। निर्भरता का निर्माण क्लास कंस्ट्रक्टर में हार्डकोड किया गया है। और सर्वोत्तम स्थिति में, हम GoogleFinder को इनहेरिट करने और इसके कंस्ट्रक्टर को ओवरराइड करने में सक्षम होंगे। और तब भी, केवल तभी जब ग्रैबर और फ़िल्टर गुणों का दायरा सुरक्षित या सार्वजनिक हो।
एक आखिरी बिंदु, हर बार जब हम एक नया GoogleFinder ऑब्जेक्ट बनाते हैं, तो मेमोरी में निर्भरता ऑब्जेक्ट्स की एक नई जोड़ी बनाई जाएगी, हालांकि हम कई GoogleFinder ऑब्जेक्ट्स में एक ग्रैबर ऑब्जेक्ट और एक HtmlExtractor ऑब्जेक्ट का आसानी से उपयोग कर सकते हैं।
मुझे लगता है कि आप पहले से ही समझते हैं कि निर्भरता आरंभीकरण को कक्षा से बाहर ले जाने की आवश्यकता है। हमें आवश्यकता हो सकती है कि पहले से तैयार निर्भरताएँ GoogleFinder क्लास के कंस्ट्रक्टर को दी जाएँ।

क्लास GoogleFinder (निजी $grabber; निजी $filter; सार्वजनिक फ़ंक्शन __construct(ग्रैबर $grabber, HtmlExtractor $filter) ($this->grabber = $grabber; $this->filter = $filter; ) सार्वजनिक फ़ंक्शन ढूंढें($searchString) (/** स्थापित परिणामों की सरणी लौटाता है */))

यदि हम अन्य डेवलपर्स को अपने स्वयं के ग्रैबर और एचटीएमएलएक्सट्रैक्टर कार्यान्वयन को जोड़ने और उपयोग करने की क्षमता देना चाहते हैं, तो हमें उनके लिए इंटरफेस शुरू करने पर विचार करना चाहिए। ऐसे में यह न सिर्फ उपयोगी है, बल्कि जरूरी भी है. मेरा मानना ​​है कि यदि हम किसी प्रोजेक्ट में केवल एक कार्यान्वयन का उपयोग करते हैं और भविष्य में नए कार्यान्वयन की उम्मीद नहीं करते हैं, तो हमें एक इंटरफ़ेस बनाने से इनकार कर देना चाहिए। स्थिति के अनुसार कार्य करना और वास्तविक आवश्यकता होने पर सरल रिफैक्टरिंग करना बेहतर है।
अब हमारे पास सभी आवश्यक कक्षाएं हैं और हम नियंत्रक में GoogleFinder क्लास का उपयोग कर सकते हैं।

क्लास कंट्रोलर (पब्लिक फंक्शन एक्शन() (/* कुछ सामान */ $फाइंडर = नया GoogleFinder(नया ग्रैबर(), नया HtmlExtractor()); $परिणाम = $फाइंडर->

आइए मध्यवर्ती परिणामों को संक्षेप में प्रस्तुत करें। हमने बहुत कम कोड लिखा और पहली नज़र में, हमने कुछ भी गलत नहीं किया। लेकिन... यदि हमें GoogleFinder जैसी किसी वस्तु का उपयोग किसी अन्य स्थान पर करने की आवश्यकता हो तो क्या होगा? हमें इसकी रचना की नकल बनानी होगी। हमारे उदाहरण में, यह केवल एक पंक्ति है और समस्या इतनी ध्यान देने योग्य नहीं है। व्यवहार में, वस्तुओं को आरंभ करना काफी जटिल हो सकता है और इसमें 10 लाइनें या इससे भी अधिक समय लग सकता है। कोड दोहराव की विशिष्ट अन्य समस्याएँ भी उत्पन्न होती हैं। यदि रीफैक्टरिंग प्रक्रिया के दौरान आपको प्रयुक्त वर्ग का नाम या ऑब्जेक्ट आरंभीकरण तर्क को बदलने की आवश्यकता है, तो आपको सभी स्थानों को मैन्युअल रूप से बदलना होगा। मुझे लगता है आप जानते हैं कि यह कैसे होता है :)
आमतौर पर, हार्डकोड को सरलता से निपटाया जाता है। डुप्लिकेट मान आमतौर पर कॉन्फ़िगरेशन में शामिल होते हैं। यह आपको उन सभी स्थानों पर मूल्यों को केंद्रीय रूप से बदलने की अनुमति देता है जहां उनका उपयोग किया जाता है।

रजिस्ट्री टेम्पलेट.

इसलिए, हमने ऑब्जेक्ट के निर्माण को कॉन्फ़िगरेशन में स्थानांतरित करने का निर्णय लिया। चलो उसे करते हैं।

$रजिस्ट्री = नया ArrayObject(); $रजिस्ट्री["ग्रैबर"] = नया ग्रैबर(); $रजिस्ट्री["फ़िल्टर"] = नया HtmlExtractor(); $रजिस्ट्री["google_finder"] = नया GoogleFinder($रजिस्ट्री["ग्रैबर"], $रजिस्ट्री["फ़िल्टर"]);
हमें बस अपना ArrayObject कंट्रोलर के पास भेजना है और समस्या हल हो गई है।

क्लास कंट्रोलर (निजी $रजिस्ट्री; सार्वजनिक फ़ंक्शन __construct(ArrayObject $रजिस्ट्री) ($यह->रजिस्ट्री = $रजिस्ट्री; ) सार्वजनिक फ़ंक्शन एक्शन() ( /* कुछ सामग्री */ $परिणाम = $यह->रजिस्ट्री["google_finder" ]->ढूंढें ("खोज स्ट्रिंग"); /* परिणामों के साथ कुछ करें */ ) )

हम रजिस्ट्री के विचार को और विकसित कर सकते हैं। ArrayObject को इनहेरिट करना, एक नई क्लास के अंदर ऑब्जेक्ट्स के निर्माण को इनकैप्सुलेट करना, इनिशियलाइज़ेशन के बाद नए ऑब्जेक्ट्स को जोड़ने पर रोक लगाना, आदि। लेकिन मेरी राय में, दिया गया कोड पूरी तरह से यह स्पष्ट कर देता है कि रजिस्ट्री टेम्पलेट क्या है। यह पैटर्न जनरेटिव नहीं है, लेकिन यह हमारी समस्याओं को हल करने का एक तरीका है। रजिस्ट्री केवल एक कंटेनर है जिसमें हम वस्तुओं को संग्रहीत कर सकते हैं और उन्हें एप्लिकेशन के भीतर भेज सकते हैं। वस्तुओं को उपलब्ध कराने के लिए, हमें पहले उन्हें बनाना होगा और उन्हें इस कंटेनर में पंजीकृत करना होगा। आइए इस दृष्टिकोण के फायदे और नुकसान पर नजर डालें।
पहली नज़र में, हमने अपना लक्ष्य हासिल कर लिया है। हमने क्लास नामों को हार्डकोड करना बंद कर दिया और ऑब्जेक्ट को एक ही स्थान पर बनाया। हम वस्तुओं को एक ही प्रतिलिपि में बनाते हैं, जो उनके पुन: उपयोग की गारंटी देता है। यदि ऑब्जेक्ट बनाने का तर्क बदलता है, तो एप्लिकेशन में केवल एक स्थान को संपादित करने की आवश्यकता होगी। बोनस के रूप में, हमें रजिस्ट्री में वस्तुओं को केंद्रीय रूप से प्रबंधित करने की क्षमता प्राप्त हुई। हम आसानी से सभी उपलब्ध वस्तुओं की एक सूची प्राप्त कर सकते हैं और उनके साथ कुछ हेरफेर कर सकते हैं। आइए अब देखें कि हमें इस टेम्पलेट के बारे में क्या पसंद नहीं आ सकता है।
सबसे पहले, हमें ऑब्जेक्ट को रजिस्ट्री में पंजीकृत करने से पहले उसे बनाना होगा। तदनुसार, "अनावश्यक वस्तुएं" बनाने की उच्च संभावना है, अर्थात। वे जो मेमोरी में बनाए जाएंगे, लेकिन एप्लिकेशन में उपयोग नहीं किए जाएंगे। हाँ, हम रजिस्ट्री में वस्तुओं को गतिशील रूप से जोड़ सकते हैं, अर्थात्। केवल वे ऑब्जेक्ट बनाएं जो किसी विशिष्ट अनुरोध को संसाधित करने के लिए आवश्यक हों। किसी न किसी तरह, हमें इसे मैन्युअल रूप से नियंत्रित करना होगा। तदनुसार, समय के साथ इसे बनाए रखना बहुत कठिन हो जाएगा।
दूसरे, हमारे पास एक नई नियंत्रक निर्भरता है। हां, हम रजिस्ट्री में स्थिर विधि के माध्यम से ऑब्जेक्ट प्राप्त कर सकते हैं ताकि हमें कंस्ट्रक्टर को रजिस्ट्री पास न करनी पड़े। लेकिन मेरी राय में आपको ऐसा नहीं करना चाहिए. किसी वस्तु के अंदर निर्भरता पैदा करने और परीक्षण में कठिनाइयों (इस विषय पर) की तुलना में स्थैतिक विधियाँ और भी अधिक मजबूत संबंध हैं।
तीसरा, नियंत्रक इंटरफ़ेस हमें इस बारे में कुछ नहीं बताता कि वह किन वस्तुओं का उपयोग करता है। हम रजिस्ट्री में उपलब्ध किसी भी वस्तु को कंट्रोलर में प्राप्त कर सकते हैं। हमारे लिए यह कहना मुश्किल होगा कि नियंत्रक किन वस्तुओं का उपयोग करता है जब तक कि हम उसके सभी स्रोत कोड की जांच नहीं कर लेते।

फ़ैक्टरी विधि

रजिस्ट्री के साथ हमारी सबसे बड़ी शिकायत यह है कि किसी वस्तु तक पहुँचने से पहले उसे आरंभीकृत किया जाना चाहिए। कॉन्फ़िगरेशन में किसी ऑब्जेक्ट को प्रारंभ करने के बजाय, हम ऑब्जेक्ट बनाने के तर्क को किसी अन्य वर्ग में अलग कर सकते हैं, जिसे हम अपनी ज़रूरत के ऑब्जेक्ट को बनाने के लिए "पूछ" सकते हैं। वे वर्ग जो वस्तुओं के निर्माण के लिए जिम्मेदार होते हैं, कारखाने कहलाते हैं। और डिज़ाइन पैटर्न को फ़ैक्टरी विधि कहा जाता है। आइए एक उदाहरण कारखाने को देखें।

क्लास फ़ैक्टरी (सार्वजनिक फ़ंक्शन getGoogleFinder() ( नया GoogleFinder लौटाएँ ($this->getGrabber(), $this->getHtmlExtractor()); ) निजी फ़ंक्शन getGrabber() ( नया Grabber() लौटाएँ; ) निजी फ़ंक्शन getHtmlExtractor() ( नया HtmlFiletr() लौटाएँ)

एक नियम के रूप में, कारखाने बनाए जाते हैं जो एक प्रकार की वस्तु बनाने के लिए जिम्मेदार होते हैं। कभी-कभी कोई फ़ैक्टरी संबंधित वस्तुओं का एक समूह बना सकती है। हम वस्तुओं को दोबारा बनाने से बचने के लिए किसी संपत्ति में कैशिंग का उपयोग कर सकते हैं।

क्लास फैक्ट्री (निजी $फाइंडर; सार्वजनिक फ़ंक्शन getGoogleFinder() ( यदि (null === $this->फाइंडर) ( $this->फाइंडर = new GoogleFinder($this->getGrabber(), $this->getHtmlExtractor() ); ) $this->फाइंडर लौटाएं; ) )

हम एक फ़ैक्टरी विधि को पैरामीटराइज़ कर सकते हैं और आने वाले पैरामीटर के आधार पर आरंभीकरण को अन्य फ़ैक्टरियों को सौंप सकते हैं। यह पहले से ही एक सार फ़ैक्टरी टेम्पलेट होगा।
यदि हमें एप्लिकेशन को मॉड्यूलराइज़ करने की आवश्यकता है, तो हमें प्रत्येक मॉड्यूल को अपनी फ़ैक्टरियाँ प्रदान करने की आवश्यकता हो सकती है। हम कारखानों की थीम को और विकसित कर सकते हैं, लेकिन मुझे लगता है कि इस टेम्पलेट का सार स्पष्ट है। आइए देखें कि हम कंट्रोलर में फ़ैक्टरी का उपयोग कैसे करेंगे।

क्लास कंट्रोलर (निजी $फैक्टरी; सार्वजनिक फ़ंक्शन __construct(फ़ैक्टरी $फ़ैक्टरी) ($यह->फ़ैक्टरी = $फ़ैक्टरी; ) सार्वजनिक फ़ंक्शन एक्शन() (/* कुछ सामग्री */ $परिणाम = $यह->फ़ैक्टरी->गेटगूगलफाइंडर( )->ढूंढें('खोज स्ट्रिंग'); /* परिणामों के साथ कुछ करें */ ) )

इस दृष्टिकोण के फायदों में इसकी सादगी शामिल है। हमारी वस्तुएं स्पष्ट रूप से बनाई गई हैं, और आपका आईडीई आपको आसानी से उस स्थान पर ले जाएगा जहां यह होता है। हमने रजिस्ट्री समस्या को भी हल कर लिया है ताकि मेमोरी में ऑब्जेक्ट केवल तभी बनाए जाएंगे जब हम फ़ैक्टरी से ऐसा करने के लिए "कहेंगे"। लेकिन हमने अभी तक यह तय नहीं किया है कि नियंत्रकों को आवश्यक कारखानों की आपूर्ति कैसे की जाए। यहां कई विकल्प हैं. आप स्थैतिक तरीकों का उपयोग कर सकते हैं. हम नियंत्रकों को आवश्यक फ़ैक्टरियाँ स्वयं बनाने दे सकते हैं और कॉपी-पेस्ट से छुटकारा पाने के हमारे सभी प्रयासों को विफल कर सकते हैं। आप फ़ैक्टरियों का एक कारखाना बना सकते हैं और केवल उसे नियंत्रक को सौंप सकते हैं। लेकिन नियंत्रक में ऑब्जेक्ट प्राप्त करना थोड़ा अधिक जटिल हो जाएगा, और आपको कारखानों के बीच निर्भरता को प्रबंधित करने की आवश्यकता होगी। इसके अलावा, यह पूरी तरह से स्पष्ट नहीं है कि अगर हम अपने एप्लिकेशन में मॉड्यूल का उपयोग करना चाहते हैं तो क्या करें, मॉड्यूल कारखानों को कैसे पंजीकृत करें, विभिन्न मॉड्यूल से कारखानों के बीच कनेक्शन कैसे प्रबंधित करें। सामान्य तौर पर, हमने कारखाने का मुख्य लाभ खो दिया है - वस्तुओं का स्पष्ट निर्माण। और हमने अभी भी "अंतर्निहित" नियंत्रक इंटरफ़ेस की समस्या का समाधान नहीं किया है।

सेवा लोकेटर

सर्विस लोकेटर टेम्पलेट आपको कारखानों के विखंडन की कमी को हल करने और वस्तुओं के निर्माण को स्वचालित और केंद्रीय रूप से प्रबंधित करने की अनुमति देता है। यदि हम इसके बारे में सोचते हैं, तो हम एक अतिरिक्त अमूर्त परत पेश कर सकते हैं जो हमारे एप्लिकेशन में ऑब्जेक्ट बनाने और इन ऑब्जेक्ट के बीच संबंधों को प्रबंधित करने के लिए जिम्मेदार होगी। इस परत को हमारे लिए ऑब्जेक्ट बनाने में सक्षम बनाने के लिए, हमें इसे यह कैसे करना है इसका ज्ञान देना होगा।
सेवा लोकेटर पैटर्न शर्तें:
  • सेवा एक तैयार वस्तु है जिसे एक कंटेनर से प्राप्त किया जा सकता है।
  • सेवा परिभाषा - सेवा आरंभीकरण तर्क।
  • एक कंटेनर (सर्विस कंटेनर) एक केंद्रीय वस्तु है जो सभी विवरणों को संग्रहीत करता है और उनके आधार पर सेवाएं बना सकता है।
कोई भी मॉड्यूल अपना सेवा विवरण पंजीकृत कर सकता है। कंटेनर से कुछ सेवा प्राप्त करने के लिए, हमें कुंजी द्वारा इसका अनुरोध करना होगा। सेवा लोकेटर को लागू करने के लिए कई विकल्प हैं; सबसे सरल संस्करण में, हम ArrayObject को एक कंटेनर के रूप में और एक क्लोजर को सेवाओं के विवरण के रूप में उपयोग कर सकते हैं।

क्लास सर्विसकंटेनर ArrayObject का विस्तार करता है (सार्वजनिक फ़ंक्शन get($key) ( यदि (is_callable($this[$key])) ( return call_user_func($this[$key]); ) नया \RuntimeException फेंकें("के तहत सेवा परिभाषा नहीं मिल सकती कुंजी [ $कुंजी ]"); ) )

तब परिभाषा पंजीकरण इस तरह दिखेगा:

$कंटेनर = नया सर्विसकंटेनर(); $कंटेनर["ग्रैबर"] = फ़ंक्शन () (नया ग्रैबर लौटाएं(); ); $कंटेनर["html_filter"] = फ़ंक्शन () (नया HtmlExtractor लौटाएं(); ); $container["google_finder"] = function() उपयोग ($container) (नया GoogleFinder लौटाएं($container->get("grabber"), $container->get("html_filter")); );

और नियंत्रक में उपयोग इस प्रकार है:

क्लास कंट्रोलर (निजी $कंटेनर; सार्वजनिक फ़ंक्शन __construct(सर्विसकंटेनर $कंटेनर) ($यह->कंटेनर = $कंटेनर; ) सार्वजनिक फ़ंक्शन एक्शन() ( /* कुछ सामग्री */ $परिणाम = $यह->कंटेनर->प्राप्त करें( "google_finder")->ढूंढें ("खोज स्ट्रिंग"); /* परिणामों के साथ कुछ करें */ ) )

एक सर्विस कंटेनर बहुत सरल हो सकता है, या यह बहुत जटिल हो सकता है। उदाहरण के लिए, सिम्फनी सर्विस कंटेनर बहुत सारी सुविधाएँ प्रदान करता है: पैरामीटर, सेवाओं के दायरे, टैग, उपनाम, निजी सेवाओं द्वारा सेवाओं की खोज, सभी सेवाओं को जोड़ने के बाद कंटेनर में परिवर्तन करने की क्षमता (कंपाइलर पास) और बहुत कुछ। DIExtraBundle मानक कार्यान्वयन की क्षमताओं का और विस्तार करता है।
लेकिन आइए अपने उदाहरण पर वापस आएं। जैसा कि आप देख सकते हैं, सर्विस लोकेटर न केवल पिछले टेम्प्लेट की तरह सभी समस्याओं का समाधान करता है, बल्कि अपनी स्वयं की सेवा परिभाषाओं के साथ मॉड्यूल का उपयोग करना भी आसान बनाता है।
इसके अलावा, फ्रेमवर्क स्तर पर हमें अमूर्तता का एक अतिरिक्त स्तर प्राप्त हुआ। अर्थात्, ServiceContainer::get विधि को बदलकर, उदाहरण के लिए, हम ऑब्जेक्ट को प्रॉक्सी से बदल सकते हैं। और प्रॉक्सी ऑब्जेक्ट के अनुप्रयोग का दायरा केवल डेवलपर की कल्पना तक सीमित है। यहां आप AOP प्रतिमान, LazyLoading आदि लागू कर सकते हैं।
लेकिन अधिकांश डेवलपर्स अभी भी सर्विस लोकेटर को एक एंटी-पैटर्न मानते हैं। क्योंकि, सिद्धांत रूप में, हमारे पास उतने ही तथाकथित हो सकते हैं कंटेनर अवेयर क्लासेस (यानी वे क्लास जिनमें कंटेनर का संदर्भ होता है)। उदाहरण के लिए, हमारा कंट्रोलर, जिसके अंदर हम कोई भी सेवा प्राप्त कर सकते हैं।
आइए देखें कि यह बुरा क्यों है।
पहले, फिर से परीक्षण। केवल परीक्षणों में उपयोग की जाने वाली कक्षाओं के लिए मॉक बनाने के बजाय, आपको पूरे कंटेनर का मॉक बनाना होगा या वास्तविक कंटेनर का उपयोग करना होगा। पहला विकल्प आपके लिए उपयुक्त नहीं है, क्योंकि... आपको परीक्षणों में बहुत सारे अनावश्यक कोड लिखने पड़ते हैं, दूसरे, क्योंकि यह इकाई परीक्षण के सिद्धांतों का खंडन करता है, और परीक्षणों को बनाए रखने के लिए अतिरिक्त लागत का कारण बन सकता है।
दूसरे, हमारे लिए रिफैक्टर करना कठिन होगा। कंटेनर में किसी भी सेवा (या सेवा परिभाषा) को बदलने से, हम सभी आश्रित सेवाओं की भी जांच करने के लिए मजबूर हो जाएंगे। और इस समस्या को IDE की मदद से हल नहीं किया जा सकता है। पूरे एप्लिकेशन में ऐसी जगहों को ढूंढना इतना आसान नहीं होगा। आश्रित सेवाओं के अलावा, आपको उन सभी स्थानों की भी जांच करने की आवश्यकता होगी जहां कंटेनर से रिफैक्टर्ड सेवा प्राप्त की जाती है।
खैर, तीसरा कारण यह है कि कंटेनर से सेवाओं के अनियंत्रित खींचने से देर-सबेर कोड में गड़बड़ी और अनावश्यक भ्रम पैदा हो जाएगा। इसे समझाना मुश्किल है, आपको यह समझने के लिए अधिक से अधिक समय खर्च करने की आवश्यकता होगी कि यह या वह सेवा कैसे काम करती है, दूसरे शब्दों में, आप पूरी तरह से समझ सकते हैं कि यह क्या करती है या कोई कक्षा कैसे काम करती है, केवल इसके संपूर्ण स्रोत कोड को पढ़कर।

डिपेंडेंसी इंजेक्शन

किसी एप्लिकेशन में कंटेनर के उपयोग को सीमित करने के लिए आप और क्या कर सकते हैं? आप नियंत्रकों सहित सभी उपयोगकर्ता ऑब्जेक्ट के निर्माण का नियंत्रण फ्रेमवर्क में स्थानांतरित कर सकते हैं। दूसरे शब्दों में, उपयोगकर्ता कोड को कंटेनर की get विधि को कॉल नहीं करना चाहिए। हमारे उदाहरण में, हम कंटेनर में नियंत्रक के लिए एक परिभाषा जोड़ सकते हैं:

$कंटेनर["google_finder"] = फ़ंक्शन() उपयोग ($कंटेनर) (नया नियंत्रक लौटाएं(ग्रैबर $ग्रैबर); );

और नियंत्रक में कंटेनर से छुटकारा पाएं:

क्लास कंट्रोलर (निजी $फाइंडर; सार्वजनिक फ़ंक्शन __construct(GoogleFinder $फाइंडर) ($यह->फाइंडर = $फाइंडर; ) सार्वजनिक फ़ंक्शन एक्शन() ( /* कुछ सामान */ $परिणाम = $यह->फाइंडर->ढूंढें( "खोज स्ट्रिंग"); /* परिणामों के साथ कुछ करें */ ) )

यह दृष्टिकोण (जब क्लाइंट वर्गों को सेवा कंटेनर तक पहुंच प्रदान नहीं की जाती है) को निर्भरता इंजेक्शन कहा जाता है। लेकिन इस टेम्पलेट के भी फायदे और नुकसान दोनों हैं। जब तक हम एकल जिम्मेदारी के सिद्धांत का पालन करते हैं, कोड बहुत सुंदर दिखता है। सबसे पहले, हमने क्लाइंट कक्षाओं में कंटेनर से छुटकारा पा लिया, जिससे उनका कोड अधिक स्पष्ट और सरल हो गया। हम आवश्यक निर्भरताओं को प्रतिस्थापित करके नियंत्रक का आसानी से परीक्षण कर सकते हैं। हम टीडीडी या बीडीडी दृष्टिकोण का उपयोग करके प्रत्येक वर्ग को दूसरों से स्वतंत्र रूप से (नियंत्रक वर्गों सहित) बना और परीक्षण कर सकते हैं। परीक्षण बनाते समय, हम कंटेनर से अलग हो सकते हैं, और बाद में जब हमें विशिष्ट उदाहरणों का उपयोग करने की आवश्यकता होती है तो एक परिभाषा जोड़ सकते हैं। यह सब हमारे कोड को सरल और स्पष्ट और परीक्षण को अधिक पारदर्शी बना देगा।
लेकिन सिक्के के दूसरे पहलू का जिक्र करना जरूरी है. तथ्य यह है कि नियंत्रक बहुत विशिष्ट वर्ग हैं। आइए इस तथ्य से शुरू करें कि नियंत्रक, एक नियम के रूप में, कार्यों का एक सेट शामिल करता है, जिसका अर्थ है कि यह एकल जिम्मेदारी के सिद्धांत का उल्लंघन करता है। परिणामस्वरूप, नियंत्रक वर्ग में किसी विशिष्ट क्रिया को निष्पादित करने के लिए आवश्यकता से अधिक निर्भरताएँ हो सकती हैं। आलसी आरंभीकरण का उपयोग करना (ऑब्जेक्ट को उसके पहले उपयोग के समय तत्काल किया जाता है, और उससे पहले एक हल्के प्रॉक्सी का उपयोग किया जाता है) कुछ हद तक प्रदर्शन समस्या को हल करता है। लेकिन वास्तुशिल्प की दृष्टि से एक नियंत्रक पर कई निर्भरताएँ बनाना भी पूरी तरह से सही नहीं है। इसके अलावा, नियंत्रकों का परीक्षण आमतौर पर एक अनावश्यक ऑपरेशन है। निस्संदेह, सब कुछ इस बात पर निर्भर करता है कि आपके एप्लिकेशन में परीक्षण कैसे व्यवस्थित किया जाता है और आप स्वयं इसके बारे में कैसा महसूस करते हैं।
पिछले पैराग्राफ से, आपने महसूस किया कि डिपेंडेंसी इंजेक्शन का उपयोग वास्तु संबंधी समस्याओं को पूरी तरह से समाप्त नहीं करता है। इसलिए, इस बारे में सोचें कि यह आपके लिए कैसे अधिक सुविधाजनक होगा, कंटेनर के लिंक को नियंत्रकों में संग्रहीत करना है या नहीं। यहां कोई एक भी सही समाधान नहीं है. मुझे लगता है कि दोनों दृष्टिकोण तब तक अच्छे हैं जब तक नियंत्रक कोड सरल रहता है। लेकिन, निश्चित रूप से, आपको नियंत्रकों के अतिरिक्त कॉनटिनर अवेयर सेवाएँ नहीं बनानी चाहिए।

निष्कर्ष

खैर, जो कुछ कहा गया है उसे संक्षेप में बताने का समय आ गया है। और बहुत कुछ कहा गया है... :)
इसलिए, वस्तुओं को बनाने के कार्य की संरचना के लिए, हम निम्नलिखित पैटर्न का उपयोग कर सकते हैं:
  • रजिस्ट्री: टेम्पलेट के स्पष्ट नुकसान हैं, जिनमें से सबसे बुनियादी है वस्तुओं को एक सामान्य कंटेनर में डालने से पहले बनाने की आवश्यकता। जाहिर है, इसके इस्तेमाल से हमें फायदे से ज्यादा परेशानियां मिलेंगी। यह स्पष्ट रूप से टेम्पलेट का सर्वोत्तम उपयोग नहीं है।
  • फ़ैक्टरी विधि: पैटर्न का मुख्य लाभ: वस्तुएं स्पष्ट रूप से बनाई जाती हैं। मुख्य नुकसान: नियंत्रकों को या तो स्वयं फ़ैक्टरियाँ बनाने के बारे में चिंता करनी होगी, जो वर्ग नामों के हार्डकोड होने की समस्या को पूरी तरह से हल नहीं करता है, या फ़्रेमवर्क को सभी आवश्यक फ़ैक्टरियों के साथ नियंत्रक प्रदान करने के लिए ज़िम्मेदार होना चाहिए, जो इतना स्पष्ट नहीं होगा। वस्तुओं को बनाने की प्रक्रिया को केंद्रीय रूप से प्रबंधित करने की कोई संभावना नहीं है।
  • सेवा लोकेटर: वस्तुओं के निर्माण को नियंत्रित करने का एक अधिक उन्नत तरीका। ऑब्जेक्ट बनाते समय आने वाले सामान्य कार्यों को स्वचालित करने के लिए अमूर्तता के एक अतिरिक्त स्तर का उपयोग किया जा सकता है। उदाहरण के लिए:
    क्लास सर्विसकंटेनर ArrayObject का विस्तार करता है (सार्वजनिक फ़ंक्शन get($key) ( if (is_callable($this[$key])) ( $obj = call_user_func($this[$key]); if ($obj इंस्टेंसof RequestAwareInterface) ($obj- >setRequest($this->get("अनुरोध")); ) $obj लौटाएं; ) नया \RuntimeException फेंकें ("कुंजी [$कुंजी] के अंतर्गत सेवा परिभाषा नहीं मिल सकती"); ) )
    सर्विस लोकेटर का नुकसान यह है कि कक्षाओं की सार्वजनिक एपीआई जानकारीपूर्ण नहीं रह जाती है। यह समझने के लिए कि इसमें किन सेवाओं का उपयोग किया जाता है, क्लास के पूरे कोड को पढ़ना आवश्यक है। एक वर्ग जिसमें एक कंटेनर का संदर्भ होता है उसका परीक्षण करना अधिक कठिन होता है।
  • डिपेंडेंसी इंजेक्शन: अनिवार्य रूप से हम पिछले पैटर्न के समान ही सर्विस कंटेनर का उपयोग कर सकते हैं। अंतर यह है कि इस कंटेनर का उपयोग कैसे किया जाता है। यदि हम कक्षाओं को कंटेनर पर निर्भर बनाने से बचते हैं, तो हमें एक स्पष्ट और स्पष्ट क्लास एपीआई मिलेगी।
मैं आपको PHP अनुप्रयोगों में ऑब्जेक्ट बनाने की समस्या के बारे में बस इतना ही नहीं बताना चाहूंगा। प्रोटोटाइप पैटर्न भी है, हमने रिफ्लेक्शन एपीआई के उपयोग पर विचार नहीं किया, हमने सेवाओं की आलसी लोडिंग की समस्या और कई अन्य बारीकियों को छोड़ दिया। लेख काफी लंबा हो गया, इसलिए मैं इसे समाप्त करूंगा :)
मैं यह दिखाना चाहता था कि डिपेंडेंसी इंजेक्शन और अन्य पैटर्न उतने जटिल नहीं हैं जितना आमतौर पर माना जाता है।
यदि हम निर्भरता इंजेक्शन के बारे में बात करते हैं, तो उदाहरण के लिए, इस पैटर्न के KISS कार्यान्वयन हैं

भविष्य के डेटाबेस की संरचना को छूना। एक शुरुआत हो चुकी है, और हम पीछे नहीं हट सकते, और मैं इसके बारे में सोचता भी नहीं हूं।

हम थोड़ी देर बाद डेटाबेस पर लौटेंगे, लेकिन अभी हम अपने इंजन के लिए कोड लिखना शुरू करेंगे। लेकिन पहले, थोड़ा सा हार्डवेयर। शुरू करना।

समय की शुरुआत

फिलहाल, हमारे पास सिस्टम के संचालन के बारे में केवल कुछ विचार और समझ हैं जिन्हें हम लागू करना चाहते हैं, लेकिन अभी तक कोई कार्यान्वयन नहीं हुआ है। हमारे पास काम करने के लिए कुछ भी नहीं है: हमारे पास कोई कार्यक्षमता नहीं है - और, जैसा कि आपको याद है, हमने इसे 2 भागों में विभाजित किया है: आंतरिक और बाहरी। वर्णमाला के लिए अक्षरों की आवश्यकता होती है, लेकिन बाहरी कार्यक्षमता के लिए आंतरिक कार्यक्षमता की आवश्यकता होती है—हम यहीं से शुरू करेंगे।

लेकिन इतनी जल्दी नहीं. इसे काम करने के लिए आपको थोड़ा और गहराई में जाने की जरूरत है। हमारा सिस्टम एक पदानुक्रम का प्रतिनिधित्व करता है, और किसी भी पदानुक्रमित प्रणाली की शुरुआत होती है: लिनक्स में एक माउंट पॉइंट, विंडोज़ में एक स्थानीय डिस्क, एक राज्य की एक प्रणाली, एक कंपनी, एक शैक्षणिक संस्थान, आदि। ऐसी प्रणाली का प्रत्येक तत्व किसी के अधीन होता है और उसके कई अधीनस्थ हो सकते हैं, और अपने पड़ोसियों और उनके अधीनस्थों को संबोधित करने के लिए यह वरिष्ठों या शुरुआत का ही उपयोग करता है। एक पदानुक्रमित प्रणाली का एक अच्छा उदाहरण एक पारिवारिक वृक्ष है: एक प्रारंभिक बिंदु चुना जाता है - कुछ पूर्वज - और हम चले जाते हैं। हमारे सिस्टम में, हमें एक शुरुआती बिंदु की भी आवश्यकता है जहाँ से हम शाखाएँ विकसित करेंगे - मॉड्यूल, प्लगइन्स, आदि। हमें किसी प्रकार के इंटरफ़ेस की आवश्यकता है जिसके माध्यम से हमारे सभी मॉड्यूल "संचार" करेंगे। आगे के काम के लिए, हमें इस अवधारणा से परिचित होने की आवश्यकता है " डिज़ाइन पैटर्न" और उनके कुछ कार्यान्वयन।

डिजाइन पैटर्न्स

यह क्या है और इसकी कितनी किस्में हैं, इसके बारे में बहुत सारे लेख हैं; विषय काफी उलझा हुआ है और मैं आपको कुछ भी नया नहीं बताऊंगा। मेरे पसंदीदा विकी पर इस विषय पर जानकारी है: एक स्लाइड वाली गाड़ी और कुछ और।

डिज़ाइन पैटर्न को अक्सर डिज़ाइन पैटर्न या केवल पैटर्न भी कहा जाता है (अंग्रेजी शब्द पैटर्न से, अनुवादित अर्थ "पैटर्न")। लेखों में आगे, जब मैं पैटर्न के बारे में बात करूंगा तो मेरा मतलब डिजाइन पैटर्न से होगा।

सभी प्रकार के डरावने (और इतने डरावने नहीं) पैटर्न नामों की विशाल सूची में से, हम अब तक केवल दो में रुचि रखते हैं: रजिस्ट्री और सिंगलटन।

रजिस्ट्री (या रजिस्टर करें) एक पैटर्न है जो एक निश्चित सरणी पर काम करता है जिसमें आप वस्तुओं के एक निश्चित सेट को जोड़ और हटा सकते हैं और उनमें से किसी और उसकी क्षमताओं तक पहुंच प्राप्त कर सकते हैं।

अविवाहित (या सिंगलटन) एक पैटर्न है जो यह सुनिश्चित करता है कि एक वर्ग का केवल एक उदाहरण मौजूद हो सकता है। इसे कॉपी नहीं किया जा सकता, सुलाया नहीं जा सकता या जगाया नहीं जा सकता (PHP जादू के बारे में बात करते हुए: __clone(), __sleep(), __wakeup())। सिंगलटन के पास एक वैश्विक पहुंच बिंदु है।

परिभाषाएँ पूर्ण या सामान्यीकृत नहीं हैं, लेकिन समझने के लिए यह पर्याप्त है। वैसे भी हमें उनकी अलग से जरूरत नहीं है. हम इनमें से प्रत्येक पैटर्न की क्षमताओं में रुचि रखते हैं, लेकिन एक वर्ग में: ऐसे पैटर्न को कहा जाता है सिंगलटन रजिस्ट्री या सिंगलटन रजिस्ट्री।

इससे हमें क्या मिलेगा?
  • हमें रजिस्ट्री के एक उदाहरण की गारंटी दी जाएगी, जिसमें हम किसी भी समय ऑब्जेक्ट जोड़ सकते हैं और कोड में कहीं से भी उनका उपयोग कर सकते हैं;
  • इसे कॉपी करना और PHP भाषा के अन्य अवांछित (इस मामले में) जादू का उपयोग करना असंभव होगा।

इस स्तर पर, यह समझना पर्याप्त है कि एक एकल रजिस्ट्री हमें सिस्टम की एक मॉड्यूलर संरचना को लागू करने की अनुमति देगी, जो कि हम लक्ष्यों पर चर्चा करते समय चाहते थे, और जैसे-जैसे विकास आगे बढ़ेगा आप बाकी को समझ जाएंगे।

ख़ैर, बहुत हो गए शब्द, चलो बनाते हैं!

पहली पंक्तियाँ

चूँकि यह वर्ग कर्नेल की कार्यक्षमता से संबंधित होगा, हम अपने प्रोजेक्ट के रूट में कोर नामक एक फ़ोल्डर बनाकर शुरुआत करेंगे जिसमें हम कर्नेल मॉड्यूल के सभी वर्गों को रखेंगे। हम रजिस्ट्री से शुरू करते हैं, तो आइए फ़ाइल को रजिस्ट्री.php कहते हैं

हमें इस संभावना में कोई दिलचस्पी नहीं है कि कोई जिज्ञासु उपयोगकर्ता ब्राउज़र लाइन में हमारी फ़ाइल का सीधा पता दर्ज करेगा, इसलिए हमें इससे खुद को बचाने की ज़रूरत है। इस लक्ष्य को प्राप्त करने के लिए, हमें बस मुख्य निष्पादन योग्य फ़ाइल में एक निश्चित स्थिरांक को परिभाषित करने की आवश्यकता है, जिसे हम जाँचेंगे। यह विचार नया नहीं है; जहाँ तक मुझे याद है, इसका उपयोग जूमला में किया गया था। यह एक सरल और काम करने वाली विधि है, इसलिए हम यहां साइकिल के बिना भी काम चला सकते हैं।

चूँकि हम किसी ऐसी चीज़ की सुरक्षा कर रहे हैं जो जुड़ी हुई है, हम स्थिरांक को _PLUGSECURE_ कहेंगे:

यदि (परिभाषित ("_PLUGSECURE_")) (डाई ("डायरेक्ट मॉड्यूल कॉल निषिद्ध है!"); )

अब, यदि आप सीधे इस फ़ाइल तक पहुँचने का प्रयास करते हैं, तो कुछ भी उपयोगी नहीं निकलेगा, जिसका अर्थ है कि लक्ष्य प्राप्त कर लिया गया है।

इसके बाद, मैं हमारे सभी मॉड्यूल के लिए एक निश्चित मानक निर्धारित करने का प्रस्ताव करता हूं। मैं प्रत्येक मॉड्यूल को एक फ़ंक्शन प्रदान करना चाहता हूं जो इसके बारे में कुछ जानकारी लौटाएगा, जैसे मॉड्यूल का नाम, और यह फ़ंक्शन क्लास में आवश्यक होना चाहिए। इस लक्ष्य को प्राप्त करने के लिए हम निम्नलिखित लिखते हैं:

इंटरफ़ेस StorableObject (सार्वजनिक स्थैतिक फ़ंक्शन getClassName(); )

इस कदर। अब, यदि हम किसी क्लास को बिना किसी फंक्शन के कनेक्ट करते हैं getClassName()हमें एक त्रुटि संदेश दिखाई देगा. मैं अभी इस पर ध्यान केंद्रित नहीं करूंगा, यह बाद में हमारे लिए उपयोगी होगा, कम से कम परीक्षण और डिबगिंग के लिए।

यह हमारी एकल रजिस्ट्री की कक्षा का समय है। हम क्लास और उसके कुछ वेरिएबल्स की घोषणा करके शुरुआत करेंगे:

क्लास रजिस्ट्री StorableObject को लागू करती है ( //मॉड्यूल नाम पठनीय निजी स्थैतिक $className = "रजिस्ट्री"; //रजिस्ट्री उदाहरण निजी स्थैतिक $उदाहरण; //वस्तुओं की सरणी निजी स्थैतिक $ऑब्जेक्ट्स = सरणी();

अब तक सब कुछ तार्किक और समझने योग्य है। अब, जैसा कि आपको याद है, हमारे पास सिंगलटन गुणों वाली एक रजिस्ट्री है, तो आइए तुरंत एक फ़ंक्शन लिखें जो हमें रजिस्ट्री के साथ इस तरह से काम करने की अनुमति देगा:

सार्वजनिक स्थैतिक फ़ंक्शन सिंगलटन() ( if(!isset(self::$instance)) ( $obj = __CLASS__; self::$instance = new $obj; ) return self::$instance; )

वस्तुतः: फ़ंक्शन जाँच करता है कि हमारी रजिस्ट्री का कोई उदाहरण मौजूद है या नहीं: यदि नहीं, तो यह इसे बनाता है और इसे वापस कर देता है; यदि यह पहले से मौजूद है, तो यह इसे वापस कर देता है। इस मामले में, हमें किसी जादू की आवश्यकता नहीं है, इसलिए सुरक्षा के लिए हम इसे निजी घोषित करेंगे:

निजी फ़ंक्शन __construct()() निजी फ़ंक्शन __clone()() निजी फ़ंक्शन __wakeup()() निजी फ़ंक्शन __sleep() ()

अब हमें अपनी रजिस्ट्री में एक ऑब्जेक्ट जोड़ने के लिए एक फ़ंक्शन की आवश्यकता है - इस फ़ंक्शन को सेटर कहा जाता है और मैंने इसे दो तरीकों से लागू करने का निर्णय लिया है ताकि यह दिखाया जा सके कि हम जादू का उपयोग कैसे कर सकते हैं और ऑब्जेक्ट जोड़ने का वैकल्पिक तरीका प्रदान कर सकते हैं। पहली विधि एक मानक फ़ंक्शन है, दूसरा __set() के जादू के माध्यम से पहले वाले को निष्पादित करता है।

//$ऑब्जेक्ट - कनेक्टेड ऑब्जेक्ट के लिए पथ //$की - रजिस्टर सार्वजनिक फ़ंक्शन 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])) ( //यदि ऐसा है, तो हम इस ऑब्जेक्ट को return self::$objects[$key]; ) ) //जादुई सार्वजनिक फ़ंक्शन __get($key) के माध्यम से एक समान विधि ( if (is_object(self::$objects[$ कुंजी])) (स्वयं वापसी: :$ऑब्जेक्ट्स[$कुंजी]; ))

सेटर की तरह, ऑब्जेक्ट तक पहुंच प्राप्त करने के लिए हमारे पास 2 समकक्ष प्रविष्टियाँ होंगी:

$रजिस्ट्री->getObject('config'); //नियमित विधि $registry->config; //PHP मैजिक फ़ंक्शन के माध्यम से __get()

चौकस पाठक तुरंत प्रश्न पूछेगा: क्यों __set() मैजिक फ़ंक्शन में मैं सिर्फ एक नियमित (गैर-मैजिक) ऑब्जेक्ट ऐडिंग फ़ंक्शन को कॉल करता हूं, लेकिन __get() गेटर में मैं उसी कॉल के बजाय getObject() फ़ंक्शन कोड को कॉपी करता हूं?ईमानदारी से कहूं तो, मैं इस प्रश्न का पर्याप्त सटीक उत्तर नहीं दे सकता, मैं सिर्फ इतना कहूंगा कि अन्य मॉड्यूल में __get() जादू के साथ काम करते समय मुझे समस्याएं थीं, लेकिन कोड को "हेड-ऑन" दोबारा लिखते समय ऐसी कोई समस्या नहीं होती है।

शायद इसीलिए मैंने अक्सर लेखों में PHP के जादुई तरीकों के प्रति निंदा और उनका उपयोग करने से बचने की सलाह देखी है।

"सभी जादू एक कीमत के साथ आते हैं।" © रम्पलेस्टिल्टस्किन

इस स्तर पर, हमारी रजिस्ट्री की मुख्य कार्यक्षमता पहले से ही तैयार है: हम रजिस्ट्री का एक उदाहरण बना सकते हैं, ऑब्जेक्ट जोड़ सकते हैं और पारंपरिक तरीकों और PHP भाषा के जादुई तरीकों का उपयोग करके उन तक पहुंच सकते हैं। "हटाने के बारे में क्या?"- हमें अभी इस फ़ंक्शन की आवश्यकता नहीं होगी, और मुझे यकीन नहीं है कि भविष्य में कुछ भी बदल जाएगा। अंत में, हम हमेशा आवश्यक कार्यक्षमता जोड़ सकते हैं। लेकिन अगर अब हम अपनी रजिस्ट्री का एक उदाहरण बनाने का प्रयास करते हैं,

$रजिस्ट्री = रजिस्ट्री::सिंगलटन();

हमें एक त्रुटि मिलेगी:

घातक त्रुटि: क्लास रजिस्ट्री में 1 अमूर्त विधि शामिल है और इसलिए इसे सार घोषित किया जाना चाहिए या शेष विधियों (StorableObject::getClassName) को लागू करना चाहिए ...

ऐसा इसलिए क्योंकि हम एक आवश्यक फ़ंक्शन लिखना भूल गए। याद रखें शुरुआत में ही मैंने एक ऐसे फ़ंक्शन के बारे में बात की थी जो मॉड्यूल का नाम लौटाता है? पूर्ण कार्यक्षमता के लिए यही जोड़ा जाना बाकी है। यह आसान है:

सार्वजनिक स्थैतिक फ़ंक्शन getClassName() (वापसी self::$className; )

अब कोई त्रुटि नहीं होनी चाहिए. मैं एक और फ़ंक्शन जोड़ने का प्रस्ताव करता हूं, इसकी आवश्यकता नहीं है, लेकिन देर-सबेर यह काम आ सकता है; हम भविष्य में इसका उपयोग जांच और डिबगिंग के लिए करेंगे। फ़ंक्शन हमारी रजिस्ट्री में जोड़े गए सभी ऑब्जेक्ट (मॉड्यूल) के नाम लौटाएगा:

सार्वजनिक फ़ंक्शन getObjectsList() ( //वह सरणी जिसे हम $names = array() लौटाएंगे; //ऑब्जेक्ट्स की सरणी से प्रत्येक ऑब्जेक्ट का नाम प्राप्त करें foreach(self::$objects as $obj) ( $names = $ obj->getClassName() ; ) //रजिस्टर मॉड्यूल का नाम सरणी में जोड़ें array_push($names, self::getClassName()); //और रिटर्न रिटर्न $names; )

बस इतना ही। इससे रजिस्टर पूरा हो गया. आइए उसके काम की जाँच करें? जाँच करते समय, हमें कुछ कनेक्ट करने की आवश्यकता होगी - एक कॉन्फ़िगरेशन फ़ाइल होने दें। एक नई core/config.php फ़ाइल बनाएं और हमारी रजिस्ट्री के लिए आवश्यक न्यूनतम सामग्री जोड़ें:

//स्थिरांक की जांच करना न भूलें यदि (!परिभाषित('_PLUGSECURE_')) (डाई('डायरेक्ट मॉड्यूल कॉल निषिद्ध है!'); ) क्लास कॉन्फिग (//मॉड्यूल नाम, पठनीय निजी स्थिर $className = 'कॉन्फिग "; सार्वजनिक स्थैतिक फ़ंक्शन getClassName() (वापसी self::$className; ))

ऐसा कुछ। अब आइए सत्यापन के लिए ही आगे बढ़ें। हमारे प्रोजेक्ट के मूल में, एक फ़ाइल Index.php बनाएं और उसमें निम्नलिखित कोड लिखें:

परिभाषित करें ("_PLUGSECURE_", सत्य); //वस्तुओं तक सीधी पहुंच से बचाने के लिए एक स्थिरांक को परिभाषित किया गया require_once "/core/registry.php"; //रजिस्टर से जुड़ा $registry = रजिस्ट्री::सिंगलटन(); // एक रजिस्टर सिंगलटन इंस्टेंस बनाया गया $registry->config = "/core/config.php"; // हमारे, अब तक बेकार, कॉन्फिगरेशन को कनेक्ट करें // कनेक्टेड मॉड्यूल के नाम प्रदर्शित करें इको " जुड़े हुए"; foreach ($रजिस्ट्री->

  • " . $नाम ।"
  • "; }

    या, यदि आप अभी भी जादू से बचते हैं, तो 5वीं पंक्ति को वैकल्पिक विधि से बदला जा सकता है:

    परिभाषित करें ("_PLUGSECURE_", सत्य); //वस्तुओं तक सीधी पहुंच से बचाने के लिए एक स्थिरांक को परिभाषित किया गया require_once "/core/registry.php"; //रजिस्टर से जुड़ा $registry = रजिस्ट्री::सिंगलटन(); // एक रजिस्टर सिंगलटन इंस्टेंस बनाया गया $registry->addObject("config", "/core/config.php"); // हमारे, अब तक बेकार, कॉन्फिगरेशन को कनेक्ट करें // कनेक्टेड मॉड्यूल के नाम प्रदर्शित करें इको " जुड़े हुए"; foreach ($registry->getObjectsList() $names के रूप में) (इको "

  • " . $नाम ।"
  • "; }

    अब ब्राउज़र खोलें और एड्रेस बार में http://localhost/index.php या बस http://localhost/ लिखें (यदि आप मानक ओपन सर्वर या समान वेब सर्वर सेटिंग्स का उपयोग कर रहे हैं तो प्रासंगिक)

    परिणामस्वरूप, हमें कुछ इस तरह देखना चाहिए:

    जैसा कि आप देख सकते हैं, कोई त्रुटि नहीं है, जिसका अर्थ है कि सब कुछ काम करता है, जिसके लिए मैं आपको बधाई देता हूं :)

    आज हम यहीं रुकेंगे. अगले लेख में, हम डेटाबेस पर लौटेंगे और MySQL SUDB के साथ काम करने के लिए एक क्लास लिखेंगे, इसे रजिस्ट्री से जोड़ेंगे और व्यवहार में काम का परीक्षण करेंगे। फिर मिलते हैं!

    यह पैटर्न, सिंगलटन की तरह, डेवलपर्स से शायद ही कभी सकारात्मक प्रतिक्रिया का कारण बनता है, क्योंकि यह अनुप्रयोगों का परीक्षण करते समय समान समस्याओं को जन्म देता है। फिर भी, वे डांटते हैं, लेकिन सक्रिय रूप से उपयोग करते हैं। सिंगलटन की तरह, रजिस्ट्री पैटर्न कई अनुप्रयोगों में पाया जाता है और, एक तरह से या किसी अन्य, कुछ समस्याओं को हल करने को बहुत सरल बनाता है।

    आइए दोनों विकल्पों पर क्रम से विचार करें।

    जिसे "शुद्ध रजिस्ट्री" या केवल रजिस्ट्री कहा जाता है, वह एक स्थिर इंटरफ़ेस वाले वर्ग का कार्यान्वयन है। सिंगलटन पैटर्न से मुख्य अंतर यह है कि यह किसी वर्ग का कम से कम एक उदाहरण बनाने की क्षमता को अवरुद्ध करता है। इसे देखते हुए, निजी या संरक्षित संशोधक के पीछे जादुई तरीकों __clone() और __wakeup() को छिपाने का कोई मतलब नहीं है।

    रजिस्ट्री वर्गदो स्थिर विधियाँ होनी चाहिए - एक गेटर और एक सेटर। सेटर दिए गए कुंजी के साथ बाइंडिंग के साथ पास किए गए ऑब्जेक्ट को स्टोरेज में रखता है। तदनुसार, प्राप्तकर्ता स्टोर से एक वस्तु लौटाता है। एक स्टोर एक सहयोगी कुंजी-मूल्य सरणी से अधिक कुछ नहीं है।

    रजिस्ट्री पर पूर्ण नियंत्रण के लिए, एक और इंटरफ़ेस तत्व पेश किया गया है - एक विधि जो आपको स्टोरेज से किसी ऑब्जेक्ट को हटाने की अनुमति देती है।

    सिंगलटन पैटर्न के समान समस्याओं के अलावा, दो और भी हैं:

    • एक अन्य प्रकार की निर्भरता का परिचय - रजिस्ट्री कुंजियों पर;
    • दो अलग-अलग रजिस्ट्री कुंजियों में एक ही ऑब्जेक्ट का संदर्भ हो सकता है

    पहले मामले में, अतिरिक्त निर्भरता से बचना असंभव है। कुछ हद तक, हम प्रमुख नामों से जुड़ जाते हैं।

    दूसरी समस्या को रजिस्ट्री::सेट() विधि में चेक शुरू करके हल किया गया है:

    सार्वजनिक स्थैतिक फ़ंक्शन सेट($कुंजी, $आइटम) ( यदि (!array_key_exists($कुंजी, स्व::$_रजिस्ट्री)) ( foreach (स्वयं::$_रजिस्ट्री $val के रूप में) ( यदि ($val === $आइटम) (नया अपवाद फेंकें ("आइटम पहले से मौजूद है"); ) ) self::$_registry[$key] = $item; ) )

    « स्वच्छ रजिस्ट्री पैटर्न"एक और समस्या को जन्म देता है - वर्ग नाम के माध्यम से सेटर और गेटर तक पहुंचने की आवश्यकता के कारण निर्भरता में वृद्धि। आप किसी ऑब्जेक्ट का संदर्भ नहीं बना सकते हैं और उसके साथ काम नहीं कर सकते हैं, जैसा कि सिंगलटन पैटर्न के मामले में था, जब यह दृष्टिकोण उपलब्ध था:

    $instance = सिंगलटन::getInstance(); $instance->Foo();

    यहां हमारे पास सिंगलटन उदाहरण के संदर्भ को सहेजने का अवसर है, उदाहरण के लिए, वर्तमान वर्ग की संपत्ति में, और ओओपी विचारधारा की आवश्यकता के अनुसार इसके साथ काम करें: इसे एकत्रित वस्तुओं के पैरामीटर के रूप में पास करें या वंशजों में इसका उपयोग करें।

    इस मुद्दे को हल करने के लिए वहाँ है सिंगलटन रजिस्ट्री कार्यान्वयन, जो बहुत से लोगों को पसंद नहीं आता क्योंकि यह अनावश्यक कोड लगता है। मुझे लगता है कि इस रवैये का कारण ओओपी के सिद्धांतों की कुछ गलतफहमी या उनके लिए जानबूझकर की गई उपेक्षा है।

    _रजिस्ट्री[$कुंजी] = $वस्तु; ) स्थिर सार्वजनिक फ़ंक्शन get($key) ( return self::getInstance()->_registry[$key]; ) निजी फ़ंक्शन __wakeup() ( ) निजी फ़ंक्शन __construct() ( ) निजी फ़ंक्शन __clone() ( ) ) ?>

    पैसे बचाने के लिए, मैंने जानबूझकर विधियों और गुणों के लिए टिप्पणी ब्लॉक छोड़ दिए। मुझे नहीं लगता कि वे आवश्यक हैं.

    जैसा कि मैंने पहले ही कहा, मूलभूत अंतर यह है कि अब रजिस्ट्री वॉल्यूम के संदर्भ को सहेजना संभव है और हर बार स्थिर तरीकों से बोझिल कॉल का उपयोग नहीं करना संभव है। यह विकल्प मुझे कुछ अधिक सही लगता है। मेरी राय से सहमत या असहमत होना ज्यादा मायने नहीं रखता, बिल्कुल मेरी राय की तरह। कोई भी कार्यान्वयन सूक्ष्मताएं उल्लिखित कई नुकसानों से पैटर्न को खत्म नहीं कर सकती हैं।

    मैंने हमारे जीवन में अक्सर उपयोग किए जाने वाले पैटर्न के बारे में संक्षेप में लिखने का फैसला किया, अधिक उदाहरण, कम पानी, आइए आगे बढ़ते हैं।

    एकाकी वस्तु

    "एकल" का मुख्य बिंदु यह है कि जब आप कहते हैं "मुझे एक टेलीफोन एक्सचेंज की आवश्यकता है," तो वे आपसे कहेंगे "यह वहां पहले ही बनाया जा चुका है," न कि "चलो इसे फिर से बनाते हैं।" एक "अकेला" हमेशा अकेला होता है।

    क्लास सिंगलटन (निजी स्थिर $उदाहरण = शून्य; निजी फ़ंक्शन __construct())( /* ... @रिटर्न सिंगलटन */ ) // नए सिंगलटन निजी फ़ंक्शन __क्लोन() ( /* ... @रिटर्न सिंगलटन के माध्यम से निर्माण से बचाव करें * / ) // निजी फ़ंक्शन की क्लोनिंग के माध्यम से निर्माण से बचाव करें __wakeup() ( /* ... @return Singleton */ ) // सार्वजनिक स्थैतिक फ़ंक्शन getInstance को अनसीरियलाइज करके निर्माण से बचाएं ( if (is_null(self::$instance) ) ) ( self::$instance = new self; ) return self::$instance; ) )

    रजिस्ट्री (रजिस्ट्री, प्रविष्टियों का जर्नल)

    जैसा कि नाम से पता चलता है, यह पैटर्न इसमें रखे गए रिकॉर्ड को संग्रहीत करने के लिए डिज़ाइन किया गया है और, तदनुसार, यदि आवश्यक हो तो इन रिकॉर्ड्स को (नाम से) वापस कर देता है। टेलीफोन एक्सचेंज के उदाहरण में, यह निवासियों के टेलीफोन नंबरों के संबंध में एक रजिस्टर है।

    क्लास रजिस्ट्री (निजी $रजिस्ट्री = सरणी(); सार्वजनिक फ़ंक्शन सेट ($कुंजी, $ऑब्जेक्ट) ($यह->रजिस्ट्री[$कुंजी] = $ऑब्जेक्ट; ) सार्वजनिक फ़ंक्शन प्राप्त करें($कुंजी) ( $यह->रजिस्ट्री वापस करें [$कुंजी]; ) )

    सिंगलटन रजिस्ट्री- भ्रमित न हों)

    "रजिस्ट्री" अक्सर "एकाकी" होती है, लेकिन यह हमेशा उसी तरह से नहीं होनी चाहिए। उदाहरण के लिए, हम लेखा विभाग में कई जर्नल बना सकते हैं, एक में "ए" से "एम" तक के कर्मचारी हैं, दूसरे में "एन" से "जेड" तक के कर्मचारी हैं। ऐसी प्रत्येक पत्रिका एक "रजिस्ट्री" होगी, लेकिन "एकल" नहीं, क्योंकि पहले से ही 2 पत्रिकाएँ हैं।

    क्लास सिंगलटनरजिस्ट्री (निजी स्थिर $उदाहरण = शून्य; निजी $रजिस्ट्री = सरणी(); निजी फ़ंक्शन __construct() ( /* ... @रिटर्न सिंगलटन */ ) // नए सिंगलटन निजी फ़ंक्शन के माध्यम से निर्माण से सुरक्षित रखें __क्लोन() ( / * ... @रिटर्न सिंगलटन */ ) // निजी फ़ंक्शन क्लोनिंग के माध्यम से निर्माण से बचाव करें __wakeup() ( /* ... @रिटर्न सिंगलटन */ ) // सार्वजनिक स्थैतिक फ़ंक्शन getInstance() को अनसीरियलाइज़ करके निर्माण से बचाएं ( यदि ( is_null(self::$instance)) ( self::$instance = new self; ) return self::$instance; ) public function set($key, $object) ( $this->registry[$key] = $ ऑब्जेक्ट; ) सार्वजनिक फ़ंक्शन प्राप्त करें ($ कुंजी) ( $ यह लौटाएं-> रजिस्ट्री [$ कुंजी]; ))

    मल्टीटन ("एकल" का पूल) या दूसरे शब्दों मेंरजिस्ट्री सिंगलटन ) - सिंगलटन रजिस्ट्री के साथ भ्रमित न हों

    अक्सर "रजिस्टर" का उपयोग विशेष रूप से "एकल" को स्टोर करने के लिए किया जाता है। लेकिन क्योंकि "रजिस्ट्री" पैटर्न "जेनरेटिव पैटर्न" नहीं है, लेकिन मैं "सिंगलटन" के संबंध में "रजिस्टर" पर विचार करना चाहूंगा।इसलिए हम एक पैटर्न लेकर आए मल्टीटन, जिसके अनुसारइसके मूल में, यह एक "रजिस्ट्री" है जिसमें कई "एकल" शामिल हैं, जिनमें से प्रत्येक का अपना "नाम" है जिसके द्वारा उस तक पहुंचा जा सकता है।

    छोटा: आपको इस वर्ग के ऑब्जेक्ट बनाने की अनुमति देता है, लेकिन केवल तभी जब आप ऑब्जेक्ट को नाम देते हैं। इसका कोई वास्तविक जीवन उदाहरण नहीं है, लेकिन मुझे इंटरनेट पर निम्नलिखित उदाहरण मिला:

    क्लास डेटाबेस (निजी स्थिर $उदाहरण = सरणी(); निजी फ़ंक्शन __construct() ( ) निजी फ़ंक्शन __clone() ( ) सार्वजनिक स्थैतिक फ़ंक्शन getInstance($key) ( if(!array_key_exists($key, self::$instances)) ( self::$instances[$key] = new self(); ) return self::$instances[$key]; ) ) $master = Database::getInstance("master"); var_dump($मास्टर); //ऑब्जेक्ट(डेटाबेस)#1 (0) ( ) $लॉगर = डेटाबेस::getInstance("लॉगर"); var_dump($लॉगर); //ऑब्जेक्ट(डेटाबेस)#2 (0) ( ) $masterDupe = डेटाबेस::getInstance("master"); var_dump($masterDupe); // ऑब्जेक्ट(डेटाबेस)#1 (0) ( ) // गंभीर त्रुटि: निजी डेटाबेस पर कॉल करें::__construct() अमान्य संदर्भ से $dbFatalError = new Database(); // PHP घातक त्रुटि: निजी डेटाबेस पर कॉल करें::__clone() $dbCloneError = क्लोन $masterDupe;

    ऑब्जेक्ट पूल

    मूलतः यही पैटर्न है एक "रजिस्ट्री" जो केवल वस्तुओं को संग्रहीत करती है, कोई स्ट्रिंग, सरणियाँ आदि नहीं। डेटा के प्रकार।

    कारखाना

    पैटर्न का सार लगभग पूरी तरह से इसके नाम से वर्णित है। जब आपको कुछ वस्तुएं, जैसे जूस के डिब्बे, प्राप्त करने की आवश्यकता होती है, तो आपको यह जानने की आवश्यकता नहीं है कि वे किसी कारखाने में कैसे बनाई जाती हैं। आप बस कहते हैं, "मुझे संतरे के रस का एक कार्टन दो," और "फ़ैक्टरी" आपको आवश्यक पैकेज लौटा देती है। कैसे? यह सब फ़ैक्टरी द्वारा ही तय किया जाता है, उदाहरण के लिए, यह पहले से मौजूद मानक की "प्रतिलिपि" बनाता है। "फ़ैक्टरी" का मुख्य उद्देश्य, यदि आवश्यक हो, तो जूस पैकेज की "उपस्थिति" की प्रक्रिया को बदलना संभव बनाना है, और उपभोक्ता को स्वयं इसके बारे में कुछ भी बताने की आवश्यकता नहीं है, ताकि वह इसके लिए अनुरोध कर सके। पहले जैसा। एक नियम के रूप में, एक कारखाना केवल एक प्रकार के "उत्पाद" के "उत्पादन" में लगा हुआ है। कार टायरों के उत्पादन को ध्यान में रखते हुए "जूस फैक्ट्री" बनाने की अनुशंसा नहीं की जाती है। जैसा कि जीवन में होता है, फ़ैक्टरी पैटर्न अक्सर एक ही व्यक्ति द्वारा बनाया जाता है।

    सार वर्ग एनिमलएब्स्ट्रैक्ट (संरक्षित $प्रजाति; सार्वजनिक फ़ंक्शन getSpecies() ( रिटर्न $यह->प्रजाति; ) ) क्लास कैट एनिमलएब्स्ट्रैक्ट (संरक्षित $प्रजाति = "बिल्ली"; ) का विस्तार करता है क्लास डॉग एनिमलएब्स्ट्रैक्ट (संरक्षित $प्रजाति = "कुत्ता"; का विस्तार करता है; ) क्लास एनिमलफैक्ट्री (पब्लिक स्टैटिक फंक्शन फैक्ट्री ($एनिमल) ( स्विच ($एनिमल) ( केस "कैट": $obj = न्यू कैट(); ब्रेक; केस "डॉग": $obj = न्यू डॉग(); ब्रेक; डिफॉल्ट : नया अपवाद फेंकें ("पशु फैक्ट्री प्रजाति का जानवर नहीं बना सकी" "। $animal . """, 1000); ) रिटर्न $obj; ) ) $cat = एनिमलफैक्ट्री::फैक्ट्री("बिल्ली"); //ऑब्जेक्ट(कैट)#1 इको $कैट->गेटस्पीशीज़(); // बिल्ली $कुत्ता = एनिमलफैक्ट्री::फैक्ट्री("कुत्ता"); //ऑब्जेक्ट(कुत्ता)#1 प्रतिध्वनि $कुत्ता->getSpecies(); // कुत्ता $हिप्पो = एनिमलफैक्ट्री::फैक्ट्री("हिप्पोपोटेमस"); // यह एक अपवाद फेंक देगा

    मैं आपका ध्यान इस तथ्य की ओर आकर्षित करना चाहूँगा कि फ़ैक्टरी विधि भी एक पैटर्न है; इसे फ़ैक्टरी विधि कहा जाता है।

    बिल्डर (निर्माता)

    तो, हम पहले ही समझ चुके हैं कि "फ़ैक्टरी" एक पेय वेंडिंग मशीन है, इसमें पहले से ही सब कुछ तैयार है, और आप बस वही कहें जो आपको चाहिए। "बिल्डर" एक ऐसा संयंत्र है जो इन पेय पदार्थों का उत्पादन करता है और इसमें सभी जटिल ऑपरेशन शामिल हैं और अनुरोध के आधार पर सरल वस्तुओं (पैकेजिंग, लेबल, पानी, स्वाद इत्यादि) से जटिल वस्तुओं को इकट्ठा कर सकता है।

    क्लास बोतल (सार्वजनिक $नाम; सार्वजनिक $लीटर; ) /** * सभी बिल्डरों को */ इंटरफ़ेस बोतलबिल्डरइंटरफ़ेस (सार्वजनिक फ़ंक्शन सेटनाम(); सार्वजनिक फ़ंक्शन सेटलिटर(); सार्वजनिक फ़ंक्शन getResult(); ) क्लास कोकाकोलाबिल्डर बोतलबिल्डरइंटरफ़ेस लागू करना चाहिए (निजी $ बोतल; सार्वजनिक फ़ंक्शन __construct() ($यह->बोतल = नई बोतल(); ) सार्वजनिक फ़ंक्शन सेटनाम($मूल्य) ($यह->बोतल->नाम = $मूल्य; ) सार्वजनिक फ़ंक्शन सेटलिटर($मूल्य) ($ यह->बोतल->लीटर = $मूल्य; ) सार्वजनिक फ़ंक्शन getResult() ( वापसी $यह->बोतल; ) ) $जूस = नया CocaColaBuilder(); $जूस->सेटनाम('कोका-कोला लाइट'); $जूस->सेटलीटर(2); $जूस->getResult();

    प्रोटोटाइप

    एक कारखाने के समान, यह वस्तुओं को बनाने का भी काम करता है, लेकिन थोड़े अलग दृष्टिकोण के साथ। कल्पना कीजिए कि आप एक बार में बीयर पी रहे थे और आपकी बीयर खत्म हो रही है, आप बारटेंडर से कहते हैं - मेरे लिए भी उसी तरह की एक और बीयर बना दो। बारटेंडर उस बीयर को देखता है जिसे आप पी रहे हैं और आपके कहे अनुसार उसकी एक प्रति बना लेता है। PHP में पहले से ही इस पैटर्न का कार्यान्वयन है, इसे कहा जाता है।

    $newJuice = क्लोन $जूस;

    आलसी आरंभीकरण

    उदाहरण के लिए, एक बॉस विभिन्न प्रकार की गतिविधियों के लिए रिपोर्टों की एक सूची देखता है और सोचता है कि ये रिपोर्टें पहले से मौजूद हैं, लेकिन वास्तव में केवल रिपोर्टों के नाम प्रदर्शित होते हैं, और रिपोर्टें अभी तक तैयार नहीं हुई हैं, और केवल तैयार की जाएंगी ऑर्डर पर (उदाहरण के लिए, रिपोर्ट देखें बटन पर क्लिक करके)। आलसी आरंभीकरण का एक विशेष मामला किसी वस्तु तक पहुंच के समय उसका निर्माण है।आप विकिपीडिया पर एक दिलचस्प पा सकते हैं, लेकिन... सिद्धांत के अनुसार, php में सही उदाहरण, उदाहरण के लिए, एक फ़ंक्शन होगा

    एडाप्टर या रैपर (एडेप्टर, रैपर)

    यह पैटर्न पूरी तरह से अपने नाम से मेल खाता है। यूरो सॉकेट के माध्यम से "सोवियत" प्लग को काम करने के लिए, एक एडाप्टर की आवश्यकता होती है। यह बिल्कुल वही है जो एक "एडेप्टर" करता है - यह दो अन्य लोगों के बीच एक मध्यवर्ती वस्तु के रूप में कार्य करता है जो एक दूसरे के साथ सीधे काम नहीं कर सकते हैं। परिभाषा के बावजूद, व्यवहार में मुझे अभी भी एडॉप्टर और रैपर के बीच अंतर दिखाई देता है।

    क्लास मायक्लास (पब्लिक फंक्शन मेथडए() () ) क्लास मायक्लासवापर ( पब्लिक फंक्शन __construct())( $this->myClass = new MyClass(); ) पब्लिक फंक्शन __call($name, $arguments)( लॉग::जानकारी(" आप $name विधि को कॉल करने वाले हैं।"); return call_user_func_array(array($this->myClass, $name), $arguments); ) ) $obj = new MyClassWrapper(); $obj->methodA();

    डिपेंडेंसी इंजेक्शन

    निर्भरता इंजेक्शन आपको कुछ कार्यक्षमता के लिए जिम्मेदारी का हिस्सा अन्य वस्तुओं में स्थानांतरित करने की अनुमति देता है। उदाहरण के लिए, यदि हमें नए कर्मियों को नियुक्त करने की आवश्यकता है, तो हम अपना स्वयं का मानव संसाधन विभाग नहीं बना सकते हैं, बल्कि एक भर्ती कंपनी पर निर्भरता का परिचय दे सकते हैं, जो बदले में, हमारे पहले अनुरोध पर "हमें एक व्यक्ति की आवश्यकता है," या तो एक के रूप में काम करेगी। मानव संसाधन विभाग स्वयं, या एक अन्य कंपनी ("सेवा लोकेटर" का उपयोग करके) ढूंढेगा जो ये सेवाएं प्रदान करेगी।
    "निर्भरता इंजेक्शन" आपको समग्र कार्यक्षमता खोए बिना कंपनी के अलग-अलग हिस्सों को स्थानांतरित करने और इंटरचेंज करने की अनुमति देता है।

    क्लास AppleJuice() // यह विधि डिपेंडेंसी इंजेक्शन पैटर्न का एक आदिम कार्यान्वयन है और आगे आप इस फ़ंक्शन को getBottleJuice())( $obj = new) देखेंगे सेब का रस सेब का रस)( वापसी $obj; ) ) $bottleJuice = getBottleJuice();

    अब कल्पना कीजिए कि अब हमें सेब का जूस नहीं, संतरे का जूस चाहिए।

    कक्षा AppleJuice() कक्षा संतरे का रस() // यह विधि डिपेंडेंसी इंजेक्शन फ़ंक्शन getBottleJuice())( $obj = new को लागू करती है संतरे का रस; // वस्तु की जांच करें, यदि उन्होंने हमें बियर (बीयर जूस नहीं है) फिसला दिया है यदि ($objinstanceof संतरे का रस)( वापसी $obj; ) )

    जैसा कि आप देख सकते हैं, हमें न केवल जूस का प्रकार बदलना पड़ा, बल्कि जूस के प्रकार की जांच भी करनी पड़ी, जो बहुत सुविधाजनक नहीं है। निर्भरता व्युत्क्रम सिद्धांत का उपयोग करना अधिक सही है:

    इंटरफ़ेस जूस () क्लास ऐप्पलजूस जूस लागू करता है () क्लास ऑरेंजजूस जूस लागू करता है () फ़ंक्शन getBottleJuice())( $obj = new OrangeJuice; // ऑब्जेक्ट की जांच करें, अगर उन्होंने हमें बीयर फिसला दी है (बीयर जूस नहीं है) अगर($obj का उदाहरण रस)( वापसी $obj; ) )

    निर्भरता व्युत्क्रम को कभी-कभी निर्भरता इंजेक्शन के साथ भ्रमित किया जाता है, लेकिन उन्हें भ्रमित करने की कोई आवश्यकता नहीं है, क्योंकि निर्भरता व्युत्क्रम एक सिद्धांत है, कोई पैटर्न नहीं।

    सेवा लोकेटर

    "सर्विस लोकेटर" "निर्भरता इंजेक्शन" को लागू करने की एक विधि है। यह आरंभीकरण कोड के आधार पर विभिन्न प्रकार की वस्तुएं लौटाता है। कार्य किसी बिल्डर, फैक्ट्री या किसी अन्य द्वारा बनाए गए हमारे जूस पैकेज को खरीदार की इच्छानुसार वहां पहुंचाने का हो। हम लोकेटर से कहते हैं "हमें एक डिलीवरी सेवा दें" और सेवा से वांछित पते पर जूस पहुंचाने के लिए कहें। आज एक सेवा है, और कल दूसरी हो सकती है। हमारे लिए यह मायने नहीं रखता कि यह कौन सी विशिष्ट सेवा है, हमारे लिए यह जानना महत्वपूर्ण है कि यह सेवा वही प्रदान करेगी जो हम इसे बताएंगे और जहां हम इसे बताएंगे। बदले में, सेवाएँ "डिलीवर" लागू करती हैं<предмет>पर<адрес>».

    अगर हम वास्तविक जीवन के बारे में बात करें, तो शायद सर्विस लोकेटर का एक अच्छा उदाहरण पीडीओ PHP एक्सटेंशन होगा, क्योंकि आज हम MySQL डेटाबेस के साथ काम करते हैं, और कल हम PostgreSQL के साथ काम कर सकते हैं। जैसा कि आप पहले ही समझ चुके हैं, हमारे वर्ग के लिए यह मायने नहीं रखता कि वह अपना डेटा किस डेटाबेस को भेजता है, यह महत्वपूर्ण है कि वह ऐसा कर सके।

    $db = नया पीडीओ(" माई एसक्यूएल:dbname=test;host=localhost", $user, $pass); $db = new PDO(" pgsql:dbname=परीक्षण होस्ट=लोकलहोस्ट", $उपयोगकर्ता, $पास);

    डिपेंडेंसी इंजेक्शन और सर्विस लोकेटर के बीच अंतर

    यदि आपने अभी तक ध्यान नहीं दिया है, तो मैं समझाना चाहूँगा। डिपेंडेंसी इंजेक्शनपरिणामस्वरूप, यह कोई सेवा नहीं (जो कहीं कुछ वितरित कर सकती है) बल्कि एक वस्तु लौटाता है जिसका डेटा यह उपयोग करता है।

    मैं आपको PHP में रजिस्ट्री पैटर्न के अपने कार्यान्वयन के बारे में बताने का प्रयास करूंगा। रजिस्ट्री वैश्विक चर के लिए एक OOP प्रतिस्थापन है, जिसे डेटा संग्रहीत करने और सिस्टम मॉड्यूल के बीच स्थानांतरित करने के लिए डिज़ाइन किया गया है। तदनुसार, यह मानक गुणों से संपन्न है - लिखना, पढ़ना, हटाना। यहाँ एक विशिष्ट कार्यान्वयन है.

    खैर, इस तरह से हमें विधियों का एक मूर्खतापूर्ण प्रतिस्थापन मिलता है $key = $value - रजिस्ट्री::सेट($कुंजी, $मूल्य) $कुंजी - रजिस्ट्री::प्राप्त करें($कुंजी) अनसेट($कुंजी) - रजिस्ट्री हटाएं::निकालें ($कुंजी) यह अभी अस्पष्ट हो गया है - यह अतिरिक्त कोड क्यों। तो, आइए अपनी कक्षा को वह करना सिखाएं जो वैश्विक चर नहीं कर सकते। चलो इसमें काली मिर्च मिलाते हैं.

    getMessage()); ) Amdy_Registry::अनलॉक("परीक्षण"); var_dump(Amdy_Registry::get("test")); ?>

    पैटर्न के विशिष्ट कार्यों में, मैंने एक वेरिएबल को परिवर्तनों से अवरुद्ध करने की क्षमता जोड़ी, यह बड़ी परियोजनाओं पर बहुत सुविधाजनक है, आप गलती से कुछ भी सम्मिलित नहीं करेंगे। उदाहरण के लिए, डेटाबेस के साथ काम करने के लिए सुविधाजनक
    परिभाषित करें ('DB_DNS', 'mysql:host=localhost;dbname= ’);
    परिभाषित करें ('DB_USER', ' ’);
    परिभाषित करें ('DB_PASSWORD', ' ’);
    परिभाषित करें ('DB_HANDLE');

    Amdy_Regisrtry::set(DB_HANDLE, नया PDO(DB_DNS, DB_USER, DB_PASSWORD));
    Amdy_Registry::lock(DB_HANDLE);

    अब कोड की व्याख्या के लिए, डेटा को स्टोर करने के लिए, हम स्टैटिक वेरिएबल $डेटा का उपयोग करते हैं, $लॉक वेरिएबल परिवर्तन के लिए लॉक की गई कुंजियों के बारे में डेटा स्टोर करता है। नेटवर्क में, हम जांचते हैं कि वेरिएबल लॉक है या नहीं और इसे बदलें या रजिस्टर में जोड़ें। हटाते समय, हम लॉक की भी जांच करते हैं; डिफ़ॉल्ट वैकल्पिक पैरामीटर के अपवाद के साथ, गेटर अपरिवर्तित रहता है। खैर, यह अपवाद प्रबंधन पर ध्यान देने योग्य है, जो किसी कारण से शायद ही कभी उपयोग किया जाता है। वैसे, मेरे पास पहले से ही अपवादों पर एक मसौदा है, लेख की प्रतीक्षा करें। नीचे परीक्षण के लिए एक ड्राफ्ट कोड है, यहां परीक्षण के बारे में एक लेख है, इसे लिखने में भी कोई दिक्कत नहीं होगी, हालांकि मैं टीडीडी का प्रशंसक नहीं हूं।

    अगले लेख में हम डेटा आरंभीकरण और "आलस्य" को लागू करके कार्यक्षमता का और विस्तार करेंगे।