कैसे पूछें और कमांड क्वेरी पृथक्करण न करें के बीच चयन कैसे करें?


25

सिद्धांत बताओ मत पूछो कहते हैं:

आपको वस्तुओं को यह बताने का प्रयास करना चाहिए कि आप उन्हें क्या करना चाहते हैं; उनसे उनके राज्य के बारे में सवाल न पूछें, निर्णय लें और फिर उन्हें बताएं कि क्या करना है।

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

"बताओ, मत पूछो" का एक सरल उदाहरण है

Widget w = ...;
if (w.getParent() != null) {
  Panel parent = w.getParent();
  parent.remove(w);
}

और बताओ संस्करण है ...

Widget w = ...;
w.removeFromParent();

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

लेकिन फिर मुझे कमांड क्वेरी सेपरेशन पैटर्न मिला, जो कहता है कि ऐसा नहीं करना चाहिए।

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

क्या ये दोनों वास्तव में एक-दूसरे के साथ हैं और मैं दोनों के बीच कैसे चयन करूं? क्या मैं इस पर व्यावहारिक प्रोग्रामर या बर्ट्रेंड मेयर के साथ जाता हूं?


1
आप बूलियन के साथ क्या करेंगे?
डेविड

1
ऐसा लगता है कि आप प्रयोज्य संतुलन के बिना कोडिंग पैटर्न में बहुत दूर तक गोताखोरी कर रहे हैं
इज़्काता

री बूलियन ... यह एक उदाहरण है जो वापस बंद करने के लिए आसान था, लेकिन नीचे लिखित ऑपरेशन के समान, लक्ष्य इसके लिए ऑपरेशन की स्थिति होना है।
दकोतह नॉर्थ

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

1
कमांड क्वेरी सेपरेशन एक सिद्धांत है, न कि एक पैटर्न। पैटर्न एक ऐसी चीज है जिसका उपयोग आप किसी समस्या को हल करने के लिए कर सकते हैं। सिद्धांत वे हैं जो आप समस्याओं का निर्माण नहीं करने के लिए अनुसरण करते हैं।
कैंडिड_ऑरेंज

जवाबों:


25

वास्तव में आपकी उदाहरण समस्या पहले से ही अपघटन की कमी को दर्शाती है।

आइए इसे थोड़ा बदल दें:

Book b = ...;
if (b.getShelf() != null) 
    b.getShelf().remove(b);

यह वास्तव में अलग नहीं है, लेकिन दोष को अधिक स्पष्ट करता है: पुस्तक को इसके शेल्फ के बारे में क्यों पता चलेगा? सीधे शब्दों में कहें, यह नहीं करना चाहिए। यह अलमारियों पर पुस्तकों की एक निर्भरता का परिचय देता है (जिसका कोई मतलब नहीं है) और परिपत्र संदर्भ बनाता है। यह सब बुरा है।

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

पुस्तक के उदाहरण पर वापस आने के लिए - लाइब्रेरियन दर्ज करें:

Librarian l = ...;
Book b = ...;
l.findShelf(b).remove(b);

कृपया समझें, कि हम अब बताने के अर्थ में नहीं पूछ रहे हैं, न पूछें

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

लाइब्रेरियन की शुरुआत के साथ, हम इस रिश्ते को अलग से मॉडल करते हैं, इसलिए चिंता को अलग करना।


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

1
@ स्कारफ्रिज: नीचे की पंक्ति में, बताओ, मत पूछो चिंताओं के उचित पृथक्करण का एक सहसंबंध है । यदि आप इसके बारे में सोचते हैं, तो मैं इस प्रश्न का उत्तर देता हूं। कम से कम मुझे ऐसा लगता होगा;)
बैक

1
@ एसकारफ्रिज रिटर्न वैल्यू के बजाय, एक फंक्शन / डेलीगेट को पास कर सकता है जिसे असफलता मिलती है। यदि सफल होता है, तो आप कर रहे हैं, है ना?
याहु जूल

2
@BlessYahu मुझे लगता है कि अधिक इंजीनियर है। मुझे व्यक्तिगत रूप से लगता है कि कमांड-क्वेरी अलगाव वास्तविक (बहु-थ्रेडेड) दुनिया में एक आदर्श से अधिक है। IMHO यह ठीक है कि साइड-इफ़ेक्ट वाली विधि किसी वैल्यू को लौटा दे, जब तक कि विधि का नाम स्पष्ट रूप से इंगित न करे, कि यह ऑब्जेक्ट की स्थिति को बदल देगा। स्टैक के पॉप () विधि पर विचार करें। लेकिन क्वेरी नाम वाली विधि के साइड-इफेक्ट्स नहीं होने चाहिए।
स्कारफ्रिज

13

यदि आपको परिणाम जानने की आवश्यकता है, तो आप करते हैं; यह आपकी आवश्यकता है।

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

उदाहरण के लिए, आपको फ़ाइल राइट ऑपरेशन (सही या गलत) के परिणाम जानने की आवश्यकता हो सकती है। यह एक विधि का एक उदाहरण है जो एक मूल्य लौटाता है, लेकिन हमेशा दुष्प्रभाव पैदा करता है; उसके आसपास कोई रास्ता नहीं है।

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

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

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


1
कोई यह तर्क दे सकता है कि आपके उदाहरण में, यदि "राइट" ऑपरेशन विफल हो जाता है, तो एक अपवाद को फेंक दिया जाना चाहिए, इसलिए "स्थिति प्राप्त करें" विधि की कोई आवश्यकता नहीं है।
डेविड

@ डेविड: फिर ओपी के उदाहरण पर वापस आते हैं, जो कि अगर वास्तव में बदलाव हुआ है तो गलत होगा, अगर यह नहीं होता। मुझे संदेह है कि आप वहां एक अपवाद फेंकना चाहते हैं।
रॉबर्ट हार्वे

यहां तक ​​कि ओपी का उदाहरण भी मेरे लिए सही नहीं लगता। या तो आप यह देखना चाहते हैं कि यदि निष्कासन संभव नहीं था, तो आप किस स्थिति में अपवाद का उपयोग करेंगे या आप तब ध्यान देना चाहेंगे जब कोई विजेट वास्तव में हटा दिया जाता है जिस स्थिति में आप ईवेंट या कॉलबैक तंत्र का उपयोग कर सकते हैं। मैं सिर्फ एक वास्तविक उदाहरण नहीं देखता, जहाँ आप "कमांड क्वेरी सेपरेशन पैटर्न" का सम्मान नहीं करना चाहते हैं
डेविड

@ डेविड: मैंने अपना उत्तर स्पष्ट करने के लिए संपादित किया है।
रॉबर्ट हार्वे

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

2

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

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

आप बाद में कैसे निपटते हैं, यह आपकी बात है। आप इसके ऊपर एक एब्सट्रैक्शन बना सकते हैं, एक विजेट प्रकार के साथ removeFromParent(), लेकिन आपको हमेशा कम-स्तर की गिरावट होनी चाहिए, अन्यथा आपकी संभवतः समय से पहले की धारणा बना रहे थे।

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


2

आप जो वर्णन करते हैं वह कमांड-क्वेरी पृथक्करण सिद्धांत के लिए एक ज्ञात "अपवाद" है।

इस लेख में मार्टिन फाउलर बताते हैं कि वह इस तरह के अपवाद की धमकी कैसे देंगे।

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

आपके उदाहरण में मैं उसी अपवाद पर विचार करूंगा।


0

कमांड-क्वेरी अलगाव आश्चर्यजनक रूप से गलतफहमी के लिए आसान है।

अगर मैं आपको अपने सिस्टम में बताता हूं कि एक कमांड है जो एक क्वेरी से एक मूल्य भी देता है और आप कहते हैं "हा! आप उल्लंघन में हैं!" तुम बंदूक उछाल रहे हो।

नहीं, वह नहीं है जो निषिद्ध है।

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

इससे कोई फर्क नहीं पड़ता कि मैं क्या करता हूं क्योंकि मैं उन्हें फिर से खोलने जा रहा हूं। इस अतिरेक का एकमात्र लाभ यह सुनिश्चित करना था कि डिजाइनर आलसी नहीं हुए और गैर-राज्य परिवर्तन क्वेरी को शामिल करना भूल गए।

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

धाराप्रवाह इंटरफेस और iDSL के "उल्लंघन" हर समय यह ओवररिएक्शन। यदि आप प्रतिक्रिया करते हैं तो बहुत सारी शक्ति को अनदेखा किया जा रहा है।

आप यह तर्क दे सकते हैं कि एक कमांड को केवल एक काम करना चाहिए और एक क्वेरी को केवल एक ही काम करना चाहिए। और कई मामलों में यह एक अच्छी बात है। कौन कहता है कि केवल एक ही प्रश्न है जो एक आदेश का पालन करना चाहिए? ठीक है, लेकिन यह कमांड-क्वेरी अलगाव नहीं है। यह एकल जिम्मेदारी सिद्धांत है।

इस तरह से देखा और एक स्टैक पॉप कुछ विचित्र अपवाद नहीं है। यह एक ही जिम्मेदारी है। पॉप केवल कमांड क्वेरी पृथक्करण का उल्लंघन करता है यदि आप झांकना प्रदान करना भूल जाते हैं।

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