अगर सिंग्लेटन्स खराब हैं तो सर्विस कंटेनर अच्छा क्यों है?


91

हम सभी जानते हैं कि सिंगलेट्स कितने बुरे हैं क्योंकि वे निर्भरता और अन्य कारणों से छिपते हैं ।

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

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

लेकिन क्यों एक सेवा प्रदाता एक शुद्ध सिंगलटन के रूप में बुरा नहीं है?

सेवा प्रदाता निर्भरता को भी छुपाता है और वे केवल पहले इस् टेंस के निर्माण को पूरा करते हैं। इसलिए मैं वास्तव में यह समझने के लिए संघर्ष कर रहा हूं कि हमें एकल के बजाय एक सेवा प्रदाता का उपयोग क्यों करना चाहिए।

पुनश्च। मुझे पता है कि निर्भरता को छिपाने के लिए मुझे DI का उपयोग करना चाहिए (जैसा कि मिस्को ने कहा है)

जोड़ना

मैं जोड़ूंगा: इन दिनों सिंगललेट्स वह बुराई नहीं हैं, PHPUnit के निर्माता ने इसे यहां समझाया:

DI + सिंगलटन समस्या हल करता है:

<?php
class Client {

    public function doSomething(Singleton $singleton = NULL){

        if ($singleton === NULL) {
            $singleton = Singleton::getInstance();
        }

        // ...
    }
}
?>

यह बहुत ही स्मार्ट है, भले ही यह सभी समस्याओं का समाधान नहीं करता है।

DI और सेवा कंटेनर के अलावा इस सहायक वस्तुओं तक पहुंचने के लिए कोई अच्छा स्वीकार्य समाधान है?


2
@ आपका संपादन गलत धारणा बना रहा है। सेबस्टियन किसी भी तरह से, सुझाव नहीं देता है कि कोड स्निपेट एक समस्या के कम एकल का उपयोग कर बना रहा है। यह कोड बनाने का सिर्फ एक तरीका है कि अन्यथा अधिक परीक्षण योग्य परीक्षण करना असंभव होगा। लेकिन यह अभी भी समस्याग्रस्त कोड है। वास्तव में, वह स्पष्ट रूप से नोट करता है: "सिर्फ इसलिए कि आप कर सकते हैं, इसका मतलब यह नहीं है कि आपको चाहिए"। सही समाधान अभी भी सिंगलनेट्स का उपयोग नहीं करना है।
गॉर्डन

3
@ वे एसओएलआईडी सिद्धांत का पालन करते हैं।
गॉर्डन

19
मैं इस दावे पर विवाद करता हूं कि एकल गाने खराब हैं। उनका दुरुपयोग किया जा सकता है, हां लेकिन किसी भी उपकरण से। एक स्केलपेल का उपयोग जीवन को बचाने या इसे समाप्त करने के लिए किया जा सकता है। झाड़ियों को रोकने के लिए एक चेनसॉ जंगलों को साफ कर सकता है या यह आपके हाथ के एक बड़े हिस्से को काट सकता है यदि आपको नहीं पता कि हम क्या कर रहे हैं। बुद्धिमानी से अपने उपकरणों का उपयोग करने का तरीका जानें नहीं है कि जिस तरह से झूठ कल्पनातीत मन - सुसमाचार के रूप में इलाज सलाह।
paxdiablo

4
@paxdiablo लेकिन वे कर रहे हैं बुरा। सिंगलेट्स SRP, OCP और DIP का उल्लंघन करते हैं। वे आपके आवेदन में वैश्विक स्थिति और तंग युग्मन का परिचय देते हैं और यह निर्भरता के बारे में आपके एपीआई को झूठ बना देगा। यह सब आपके कोड की स्थिरता, पठनीयता और परीक्षणशीलता को नकारात्मक रूप से प्रभावित करेगा। ऐसे दुर्लभ मामले हो सकते हैं जहां ये कमियां थोड़ा फायदा देती हैं, लेकिन मेरा तर्क है कि 99% में आपको एक सिंगलटन की जरूरत नहीं है। विशेष रूप से PHP में, जहां सिंगलटन केवल अनुरोध वाले मार्गों के लिए अद्वितीय हैं और यह एक बिल्डर से सहयोगी रेखांकन को इकट्ठा करने के लिए सरल है।
गॉर्डन

5
नहीं, मैं ऐसा नहीं मानता। एक उपकरण एक फ़ंक्शन को अंजाम देने का एक साधन है, आमतौर पर इसे किसी भी तरह से आसान बनाकर, हालांकि कुछ (emacs?) को इसे कठिन बनाने का दुर्लभ अंतर है :-) इसमें, एक सिंगलटन संतुलित पेड़ या संकलक से अलग नहीं है । यदि आपको किसी वस्तु की केवल एक प्रति सुनिश्चित करने की आवश्यकता है, तो एक सिंगलटन यही करता है। चाहे यह अच्छी तरह से किया जाए पर बहस हो सकती है लेकिन मुझे विश्वास नहीं है कि आप यह तर्क दे सकते हैं कि यह ऐसा नहीं करता है। और बेहतर तरीके हो सकते हैं, जैसे कि एक चेनसॉ एक हाथ से तेज, या एक नेलगन बनाम एक हथौड़ा। यह एक उपकरण के कम / हथौड़ा नहीं करता है।
पैक्सिडाब्लो

जवाबों:


76

सेवा लोकेटर कहने के लिए सिर्फ दो बुराइयों का कम है। "कम" इन चार अंतरों के लिए उबल रहा है ( कम से कम मैं अभी किसी अन्य के बारे में नहीं सोच सकता )

एकल जिम्मेदारी सिद्धांत

सर्विस कंटेनर एकल जिम्मेदारी सिद्धांत का उल्लंघन नहीं करता है, जैसे सिंगलटन करता है। सिंगलटन ऑब्जेक्ट निर्माण और व्यावसायिक तर्क को मिलाते हैं, जबकि सेवा कंटेनर आपके एप्लिकेशन के ऑब्जेक्ट जीवन चक्रों के प्रबंधन के लिए सख्ती से जिम्मेदार है। उस संबंध में सेवा कंटेनर बेहतर है।

युग्मन

स्टैटिक मेथड कॉल्स की वजह से सिंगलटन आपके एप्लिकेशन में आमतौर पर हार्डकोडेड होते हैं, जो आपके कोड में निर्भरता को बढ़ाने के लिए कड़ा युग्मित और कठिन होता है । दूसरी ओर SL सिर्फ एक वर्ग है और इसे इंजेक्ट किया जा सकता है। तो जबकि आपके सभी वर्गीकृत इस पर निर्भर होंगे, कम से कम यह एक कपल पर निर्भरता है। इसलिए जब तक आपने सर्विसलॉकर को सिंगलटन के रूप में लागू नहीं किया, यह कुछ हद तक बेहतर है और परीक्षण के लिए भी आसान है।

हालाँकि, ServiceLocator का उपयोग करने वाले सभी वर्ग अब ServiceLocator पर निर्भर होंगे, जो युग्मन का एक रूप भी है। यह ServiceLocator के लिए एक इंटरफ़ेस का उपयोग करके कम किया जा सकता है ताकि आप एक ठोस ServiceLocator कार्यान्वयन के लिए बाध्य न हों, लेकिन आपकी कक्षाएं किसी प्रकार के लोकेटर के अस्तित्व पर निर्भर करेंगी जबकि नाटकीय रूप से बढ़े हुए ServiceLocator का उपयोग न करें।

छिपी हुई निर्भरता

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

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

ग्लोबल स्टेट

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

बहुत अधिक चर्चा के लिए सेवा लोकेटर बनाम निर्भरता इंजेक्शन पर फाउलर भी देखें ।


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


1
विशेष रूप से यहाँ परीक्षण क्षमता को लागू किया जाना चाहिए। आप स्थैतिक विधि कॉल का मजाक नहीं उड़ा सकते। हालांकि आप ऐसी सेवाओं का उपयोग कर सकते हैं, जिन्हें कंस्ट्रक्टर या सेटर के माध्यम से इंजेक्ट किया गया था।
डेविड

44

सेवा लोकेटर पैटर्न एक विरोधी पैटर्न है। यह निर्भरता को उजागर करने की समस्या को हल नहीं करता है (आप किसी वर्ग की परिभाषा को देखने से नहीं बता सकते हैं कि इसकी निर्भरताएं क्या हैं क्योंकि उन्हें इंजेक्शन नहीं दिया जा रहा है, इसके बजाय उन्हें सेवा लोकेटर से बाहर रखा जा रहा है)।

तो, आपका सवाल है: सेवा लोकेटर अच्छे क्यों हैं? मेरा उत्तर है: वे नहीं हैं।

बचना, बचना, बचना।


6
लगता है कि आप इंटरफेस के बारे में कुछ नहीं जानते हैं। क्लास बस कंस्ट्रक्टर के हस्ताक्षर में आवश्यक इंटरफ़ेस का वर्णन करता है - और यह वह सब है जो उसे जानना आवश्यक है। उत्तीर्ण सेवा लोकेटर को इंटरफ़ेस लागू करना चाहिए, बस। और अगर IDE इंटरफ़ेस के कार्यान्वयन की जांच करेगा, तो किसी भी परिवर्तन को नियंत्रित करना बहुत आसान होगा।
OZ_

4
@ Yes123: जो लोग कहते हैं कि गलत हैं, और वे गलत हैं क्योंकि SL एक विरोधी पैटर्न है। आपका सवाल है "एसएल अच्छे क्यों हैं?" मेरा उत्तर है: वे नहीं हैं।
जेसन

5
मैं यह तर्क नहीं दूंगा कि एसएल एक एनेट-पैटर्न है या नहीं, लेकिन मैं जो कहूंगा वह सिंगलटन और ग्लोबल्स की तुलना में बुराइयों से बहुत कम है। आप एक वर्ग का परीक्षण नहीं कर सकते हैं जो एक सिंगलटन पर निर्भर करता है, लेकिन आप निश्चित रूप से एक वर्ग का परीक्षण कर सकते हैं जो एक एसएल पर निर्भर करता है (एल्बिएट आप SL डिजाइन को उस बिंदु तक पेंच कर सकते हैं जहां यह काम नहीं करता है) ... इसलिए यह मूल्य है नोटिंग ...
ircmaxell

3
@ जेसन आपको ऑब्जेक्ट को पास करना होगा जो इंटरफ़ेस को लागू करता है - और यह केवल वही है जो आपको जानना चाहिए। आप केवल क्लास कंस्ट्रक्टर की परिभाषा से खुद को सीमित कर रहे हैं और कंस्ट्रक्टर को सभी कक्षाओं में लिखना चाहते हैं (इंटरफेस नहीं) - यह बेवकूफ विचार है। आप सभी की जरूरत है इंटरफ़ेस। आप सफलतापूर्वक मॉक के साथ इस वर्ग का परीक्षण कर सकते हैं, आप आसानी से कोड को बदले बिना व्यवहार को बदल सकते हैं, कोई अतिरिक्त निर्भरता और युग्मन नहीं है - यह सब (सामान्य रूप में) है जो हम डिपेंडेंसी इंजेक्शन में करना चाहते हैं।
OZ_

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

4

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

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

यह SO का एक अच्छा सवाल है जो दोनों के अंतर को समझाता है: डिपेंडेंसी इंजेक्शन और सर्विस लोकेटर पैटर्न में क्या अंतर है?


"डीआईसी आपके लिए उपयुक्त निर्भरता को इंजेक्ट करता है" क्या यह सिंगलटन के साथ भी नहीं होता है?
गतिशील

5
@ Yes123 - यदि आप एक सिंगलटन का उपयोग कर रहे हैं, तो आप इसे इंजेक्ट नहीं करेंगे, अधिकांश समय आप इसे विश्व स्तर पर एक्सेस करेंगे (यह सिंगलटन की बात है)। मुझे लगता है कि यदि आप कहते हैं कि यदि आप सिंगलटन को इंजेक्ट करते हैं, तो यह निर्भरता को नहीं छिपाएगा, लेकिन यह सिंगलटन पैटर्न के मूल उद्देश्य को हरा देता है - आप खुद से पूछेंगे, अगर मुझे विश्व स्तर पर इस वर्ग तक पहुंचने की आवश्यकता नहीं है, तो क्यों? क्या मुझे इसे सिंगलटन बनाने की आवश्यकता है?
रिक्क्रिस्टी

2

क्योंकि आप आसानी से सेवा कंटेनर में वस्तुओं को
1 से बदल सकते हैं ) विरासत (वस्तु प्रबंधक वर्ग को विरासत में मिला जा सकता है और तरीकों को खत्म किया जा सकता है)
2) बदलते विन्यास (सिम्फनी के मामले में)

और, सिंगलेट्स न केवल उच्च युग्मन के कारण खराब हैं, बल्कि इसलिए कि वे _ सिंगल _टन हैं। यह लगभग सभी प्रकार की वस्तुओं के लिए गलत वास्तुकला है।

'शुद्ध' DI (कंस्ट्रक्टर्स में) के साथ आप बहुत बड़ी कीमत अदा करेंगे - सभी ऑब्जेक्ट्स को कंस्ट्रक्टर में पास होने से पहले बनाया जाना चाहिए। इसका मतलब अधिक उपयोग की जाने वाली मेमोरी और कम प्रदर्शन होगा। इसके अलावा, हमेशा ऑब्जेक्ट को केवल निर्माता में नहीं बनाया और पारित किया जा सकता है - निर्भरता की श्रृंखला बनाई जा सकती है ... मेरी अंग्रेजी पूरी तरह से उस बारे में चर्चा करने के लिए पर्याप्त नहीं है, इसके बारे में सिम्फनी प्रलेखन में पढ़ें।


0

मेरे लिए, मैं एक सामान्य कारण के लिए वैश्विक स्थिरांक, एकल से बचने की कोशिश करता हूं, ऐसे मामले हैं जब मुझे एपीआई चलाने की आवश्यकता हो सकती है।

उदाहरण के लिए, मेरे पास फ्रंट-एंड और एडमिन है। व्यवस्थापक के अंदर, मैं चाहता हूं कि वे एक उपयोगकर्ता के रूप में लॉगिन करने में सक्षम हों। व्यवस्थापक के अंदर कोड पर विचार करें।

$frontend = new Frontend();
$frontend->auth->login($_GET['user']);
$frontend->redirect('/');

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

सिंगलटन का मेरा विचार है - आप एक ही वस्तु को दो बार माता-पिता के अंदर नहीं जोड़ सकते। उदाहरण के लिए

$logger1=$api->add('Logger');
$logger2=$api->add('Logger');

आपको एक उदाहरण के साथ छोड़ देगा और दोनों चर इसकी ओर इशारा करते हैं।

अंत में यदि आप ऑब्जेक्ट ओरिएंटेड डेवलपमेंट का उपयोग करना चाहते हैं, तो कक्षाओं के साथ नहीं, वस्तुओं के साथ काम करें।


1
तो आपकी विधि $api आपके ढांचे के आसपास के संस्करण को पारित करने के लिए है ? मैं वास्तव में आप क्या मतलब नहीं मिला। इसके अलावा अगर कॉल add('Logger')उसी उदाहरण को देता है जो मूल रूप से आपके पास एक सेवा खातिर है
गतिशील

हाँ यह सही है। मैं उन्हें "सिस्टम नियंत्रक" के रूप में संदर्भित करता हूं और वे एपीआई की कार्यक्षमता बढ़ाने के लिए हैं। इसी तरह एक मॉडल में "ऑडिटेबल" कंट्रोलर को दो बार जोड़ना ठीक उसी तरह से काम करेगा - केवल एक उदाहरण और ऑडिट फ़ील्ड्स का केवल एक सेट बनाएं।
रोमनिंश
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.