MVC: क्या नियंत्रक एकल जिम्मेदारी सिद्धांत को तोड़ता है?


16

एकल उत्तरदायित्व सिद्धांत कहता है कि "एक वर्ग में परिवर्तन का एक कारण होना चाहिए"।

एमवीसी पैटर्न में, कंट्रोलर का काम व्यू और मॉडल के बीच मध्यस्थता करना है। यह GUI पर उपयोगकर्ता द्वारा किए गए कार्यों की रिपोर्ट करने के लिए दृश्य के लिए एक इंटरफ़ेस प्रदान करता है (जैसे कि दृश्य को कॉल करने की अनुमति देता है controller.specificButtonPressed()), और यह डेटा को हेरफेर करने के लिए मॉडल पर उपयुक्त तरीकों को कॉल करने में सक्षम है या इसे संचालित करने के लिए आमंत्रित करता है (जैसे model.doSomething()) ।

इस का मतलब है कि:

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

इसका अर्थ है कि परिवर्तन के दो कारण हैं : GUI में परिवर्तन, और buisness लॉजिक में परिवर्तन।

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

और अगर मॉडल में व्यावसायिक तर्क बदलता है, तो नियंत्रक को मॉडल पर सही तरीकों को लागू करने के लिए बदलना पड़ सकता है।

इसलिए, नियंत्रक के पास बदलने के दो संभावित कारण हैं । क्या यह एसआरपी को तोड़ता है?


2
एक नियंत्रक एक 2-रास्ता सड़क है, यह आपके विशिष्ट टॉप-डाउन या बॉटम-अप दृष्टिकोण नहीं है। इसकी निर्भरता में से किसी एक को अमूर्त करने की कोई संभावना नहीं है क्योंकि नियंत्रक स्वयं अमूर्त है। पैटर्न की प्रकृति के कारण यहां एसआरपी का पालन करना संभव नहीं है। संक्षेप में: हाँ, यह SRP का उल्लंघन करता है लेकिन यह अपरिहार्य है।
जीरो वेनवेल

1
सवाल का क्या मतलब है? यदि हम सभी "हां, यह करता है" का जवाब देते हैं, तो क्या? यदि उत्तर "नहीं" है तो क्या होगा? इस समस्या को हल करने के लिए आप वास्तविक समस्या क्या है?
ब्रायन ओकले

1
"बदलने का एक कारण" का मतलब "कोड जो बदलता है" नहीं है। यदि आप कक्षा के लिए अपने चर नामों में सात टाइपो बनाते हैं, तो क्या उस कक्षा में अब 7 जिम्मेदारियां हैं? यदि आपके पास एक से अधिक चर या एक से अधिक फ़ंक्शन हैं, तो भी आपके पास केवल एक ही जिम्मेदारी हो सकती है।
बॉब

जवाबों:


14

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

MVC पर वापस: यदि मॉडल और दृश्य के बीच मध्यस्थता एक जिम्मेदारी नहीं बल्कि दो है, तो उन दोनों को मिलाकर अगली अमूर्त परत क्या होगी? यदि आप ऐसा नहीं पाते हैं जो आपने या तो इसे सही तरीके से अमूर्त नहीं किया है या ऐसा कोई भी नहीं है जिसका अर्थ है कि आपको यह सब ठीक लगा। और मुझे लगता है कि एक नियंत्रक के साथ मामला है, एक दृश्य और एक मॉडल को संभालना।


1
बस एक जोड़ा नोट के रूप में। यदि आपके पास कंट्रोलर.makeBreakfast () और कंट्रोलर .wakeUpFamily () है तो वह कंट्रोलर SRP को तोड़ रहा होगा, लेकिन इसलिए नहीं कि यह कंट्रोलर है, सिर्फ इसलिए कि इसकी एक से अधिक जिम्मेदारी है।
बॉब

उत्तर देने के लिए धन्यवाद, निश्चित नहीं कि मैं आपका अनुसरण कर रहा हूं। क्या आप सहमत हैं कि नियंत्रक की एक से अधिक जिम्मेदारी है? मेरे विचार से ऐसा इसलिए है क्योंकि इसके बदलने के दो कारण हैं (मुझे लगता है): मॉडल में बदलाव और दृश्य में बदलाव। क्या आप इस बात से सहमत हैं?
अवीव कोहन

1
हां, मैं सहमत हो सकता हूं कि नियंत्रक की एक से अधिक जिम्मेदारी है। हालाँकि ये "कम" जिम्मेदारियाँ हैं और यह कोई समस्या नहीं है क्योंकि उच्चतम स्तर पर यह केवल एक ज़िम्मेदारी है (आपके द्वारा उल्लेखित निचले लोगों का संयोजन) और इसलिए यह SRP का उल्लंघन नहीं करता है।
वैलेंट्री

1
अमूर्तता का सही स्तर खोजना निश्चित रूप से महत्वपूर्ण है। "नाश्ता बनाना" उदाहरण एक अच्छा है - एक ही जिम्मेदारी को पूरा करने के लिए अक्सर कई कार्य होते हैं जिन्हें पूरा करना चाहिए। जब तक नियंत्रक केवल इन कार्यों को ऑर्केस्ट्रेट कर रहा है, यह SRP का अनुसरण करता है। लेकिन अगर यह उबलते अंडे, टोस्ट बनाने या चाय बनाने के बारे में बहुत अधिक जानता है, तो यह एसआरपी का उल्लंघन करेगा।
एलन

यह जवाब मुझे समझ में आता है। थैंक यू वालेंटर्री।
J86

9

यदि किसी वर्ग के "बदलने के दो संभावित कारण हैं", तो हाँ, यह एसआरपी का उल्लंघन करता है।

नियंत्रक आमतौर पर हल्का होना चाहिए और कुछ GUI- संचालित घटना के जवाब में डोमेन / मॉडल में हेरफेर करने की एकल जिम्मेदारी है। हम इनमें से प्रत्येक जोड़तोड़ पर विचार कर सकते हैं कि मूल रूप से मामलों या सुविधाओं का उपयोग किया जाए।

यदि GUI पर एक नया बटन जोड़ा जाता है, तो नियंत्रक को केवल तभी बदलना होगा यदि वह नया बटन कुछ नई सुविधा का प्रतिनिधित्व करता है (यानी केवल उसी बटन का विरोध जो स्क्रीन 1 पर मौजूद था, लेकिन स्क्रीन 2 पर अभी तक मौजूद नहीं है, और यह तब है स्क्रीन 2 में जोड़ा गया)। इस नई कार्यक्षमता / सुविधा का समर्थन करने के लिए मॉडल में भी एक नया परिवर्तन होना चाहिए। नियंत्रक के पास अभी भी कुछ GUI- संचालित घटना के जवाब में डोमेन / मॉडल में हेरफेर करने की जिम्मेदारी है।

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

यदि नियंत्रक को बदलना है क्योंकि, कहते हैं, दृढ़ता की परत एक फ्लैट फ़ाइल से डेटाबेस में बदल जाती है, तो नियंत्रक निश्चित रूप से एसआरपी का उल्लंघन कर रहा है। यदि नियंत्रक हमेशा अमूर्तता की एक ही परत पर काम करता है, तो वह एसआरपी प्राप्त करने में मदद कर सकता है।


4

नियंत्रक SRP का उल्लंघन नहीं करता है। जैसा कि आप कहते हैं, इसकी जिम्मेदारी मॉडल और दृश्य के बीच मध्यस्थता करना है।

कहा जा रहा है, आपके उदाहरण के साथ मुद्दा यह है कि आप नियंत्रक विधियों को तर्क में बांध रहे हैं, अर्थात controller.specificButtonPressed। इस तरह से तरीकों का नामकरण आपके जीयूआई के लिए नियंत्रक को जोड़ता है, आप ठीक से अमूर्त चीजों में विफल रहे हैं। नियंत्रक को विशिष्ट कार्यों के प्रदर्शन के बारे में होना चाहिए, controller.saveDataया controller.retrieveEntry। जीयूआई में एक नया बटन जोड़ने का मतलब यह नहीं है कि नियंत्रक के लिए एक नया तरीका जोड़ना है।

दृश्य में एक बटन दबाने का मतलब कुछ करना है लेकिन जो कुछ भी आसानी से हो सकता है वह किसी भी अन्य मार्ग में ट्रिगर किया जा सकता है या यहां तक ​​कि दृश्य के माध्यम से भी नहीं।

एसआरपी के बारे में विकिपीडिया लेख से

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

कंट्रोलर को इस बात से कोई सरोकार नहीं होता है कि केवल उस दृश्य में क्या है जब उसके तरीकों में से एक को यह कहा जाता है कि यह दृश्य को निर्दिष्ट डेटा प्रदान करता है। इसे केवल मॉडल की कार्यक्षमता के बारे में जानने की आवश्यकता है क्योंकि यह जानता है कि इसे उन तरीकों को कॉल करने की आवश्यकता है जो उनके पास होंगे। यह इससे ज्यादा कुछ नहीं जानता।

यह जानना कि एक वस्तु को कॉल करने के लिए एक विधि उपलब्ध है, इसकी कार्यक्षमता जानने के समान नहीं है।


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

1
यदि मैं एक बटन प्रेस के बीच पूर्ण पृथक्करण के विचार को खोदता हूं और इसका अर्थ है (जो नियंत्रक विधियों की तरह है specificButtonPressed()), तो वास्तव में नियंत्रक जीयूआई से बंधा नहीं होगा। क्या मुझे specificButtonPressed()विधियों को खोदना चाहिए ? क्या मुझे लगता है कि इन तरीकों के होने से आपको फायदा होता है? या buttonPressed()नियंत्रक में विधियाँ होने लायक नहीं है?
अवीव कोहन

1
दूसरे शब्दों में (लंबी टिप्पणियों के लिए खेद है): मुझे लगता है कि specificButtonPressed()नियंत्रक में विधियों के होने का लाभ यह है कि यह व्यू को इसके बटन के अर्थ से पूरी तरह से अलग करता है । हालांकि, नुकसान यह है कि यह एक अर्थ में नियंत्रक को जीयूआई से जोड़ता है। कौन सा दृष्टिकोण बेहतर है?
अवीव कोहन

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

1

एक नियंत्रक एकल जिम्मेदारी वह अनुबंध होना है जो दृश्य और मॉडल के बीच मध्यस्थता करता है। प्रदर्शन के लिए दृश्य केवल जिम्मेदार होना चाहिए, मॉडल केवल व्यावसायिक तर्क के लिए जिम्मेदार होना चाहिए। यह उन दो जिम्मेदारियों को पाटने के लिए नियंत्रकों की जिम्मेदारी है।

यह सब अच्छी तरह से और अच्छा है, लेकिन शिक्षा से थोड़ा दूर होने के लिए; एमवीसी में एक नियंत्रक आमतौर पर कई छोटी कार्रवाई विधियों से युक्त होता है। ये क्रिया आम तौर पर एक चीज़ को करने के लिए मेल खाती है। अगर मैं उत्पाद बेच रहा हूं, तो मेरे पास शायद एक ProductController होगा। उस नियंत्रक के पास GetReviews, ShowSpecs, AddToCart ect जैसी क्रियाएं होंगी ...

व्यू में UI प्रदर्शित करने का SRP है, और उस UI के हिस्से में एक बटन शामिल है जो AddToCart कहता है।

नियंत्रक के पास प्रक्रिया में शामिल सभी दृश्यों और मॉडलों को जानने का SRP है।

कंट्रोलर AddToCart एक्शन में सभी को जानने की विशिष्ट SRP है जो किसी आइटम को कार्ट में जोड़े जाने पर शामिल करने की आवश्यकता होती है।

उत्पाद मॉडल में मॉडलिंग उत्पाद तर्क का SRP है, और ShoppingCart मॉडल में मॉडलिंग का SRP है कि आइटम कैसे बाद में चेकआउट के लिए सहेजे जाते हैं। उपयोगकर्ता मॉडल में उपयोगकर्ता का मॉडलिंग का SRP है जो अपनी कार्ट में सामान जोड़ रहा है।

आपको अपना व्यवसाय करने के लिए मॉडल का पुन: उपयोग करना चाहिए और उन मॉडलों को आपके कोड में किसी बिंदु पर युग्मित करने की आवश्यकता है। नियंत्रक प्रत्येक अनूठे तरीके को नियंत्रित करता है जो युग्मन होता है।


0

नियंत्रकों के पास वास्तव में केवल एक जिम्मेदारी है: उपयोगकर्ता के इनपुट के आधार पर अनुप्रयोगों की स्थिति में फेरबदल।

एक नियंत्रक मॉडल की स्थिति को अपडेट करने के लिए मॉडल को कमांड भेज सकता है (उदाहरण के लिए, दस्तावेज़ संपादित करना)। यह मॉडल के दृश्य की प्रस्तुति को बदलने के लिए अपने संबद्ध दृश्य को कमांड भी भेज सकता है (उदाहरण के लिए, दस्तावेज़ के माध्यम से स्क्रॉल करके)।

source: wikipedia

यदि इसके बजाय आप रेल-स्टाइल "कंट्रोलर" (जो सक्रिय रिकॉर्ड इंस्टेंसेस और डंबल टेम्प्लेट को जोड़ते हैं) हैं , तो निश्चित रूप से एसआरपी को तोड़ रहे हैं।

फिर से, रेल-शैली के अनुप्रयोग वास्तव में MVC के साथ शुरू नहीं होते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.