क्या पासिंग मापदंडों का (एंटी) पैटर्न के लिए एक नाम है जो केवल कॉल श्रृंखला में कई स्तरों का उपयोग किया जाएगा?


208

मैं कुछ विरासत कोड में वैश्विक चर के उपयोग के विकल्प खोजने की कोशिश कर रहा था। लेकिन यह सवाल तकनीकी विकल्पों के बारे में नहीं है, मैं मुख्य रूप से शब्दावली के बारे में चिंतित हूं ।

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

higherlevel(newParam)->level1(newParam)->level2(newParam)->level3(newParam)

जहां newParamपहले मेरे उदाहरण में एक वैश्विक चर था, लेकिन इसके बजाय पहले से हार्डकोड किया गया मान हो सकता है। मुद्दा यह है कि अब newParam का मूल्य प्राप्त किया गया है higherlevel()और सभी तरह से "यात्रा" करना है level3()

मैं सोच रहा था कि क्या इस तरह की स्थिति / पैटर्न के लिए एक नाम (ओं) का होना चाहिए , जहां आपको कई कार्यों के लिए एक पैरामीटर जोड़ने की आवश्यकता है जो कि मान को अनमॉडिफाइड "पास" करते हैं।

उम्मीद है, उचित शब्दावली का उपयोग करने से मुझे रीडिज़ाइन के समाधान के बारे में अधिक संसाधन खोजने और सहकर्मियों को इस स्थिति का वर्णन करने की अनुमति मिलेगी।


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

8
यह एक विशिष्ट उत्तर के लिए बहुत व्यापक स्पेक्ट्रम की तरह है। इस स्तर पर, मैं इसे सिर्फ "कोडिंग" कहूंगा।
मचाडो

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

7
हालांकि मैं इस बारे में चर्चा की सराहना करता हूं कि क्या यह एक अच्छा पैटर्न / एंटीपैटर्न / अवधारणा / समाधान है, जो मैं वास्तव में जानना चाहता था कि क्या इसके लिए कोई नाम है।
ecerulm

3
मैंने यह भी सुना है कि इसे थ्रेडिंग कहा जाता है, लेकिन प्लंबिंग लाइन को कॉल स्टैक के दौरान कम करने के साथ-साथ प्लंबिंग भी ।
19-28 बजे wchargin

जवाबों:


202

डेटा को "ट्रम्प डेटा" कहा जाता है । यह एक "कोड गंध" है, यह दर्शाता है कि कोड का एक टुकड़ा एक और कोड के कोड के साथ कुछ दूरी पर बिचौलियों के माध्यम से संचार कर रहा है।

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

वैश्विक चर निकालने के लिए रिफ़ेक्ट करना मुश्किल है, और ट्रम्प डेटा ऐसा करने का एक तरीका है, और अक्सर सबसे सस्ता तरीका है। इसकी लागत है।


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

9
@ जिमीजैम, वे कार्य निश्चित रूप से सामान करते हैं। बस उस विशिष्ट नए पैरामीटर के साथ नहीं जो पहले सिर्फ एक वैश्विक था।
बजे

174
प्रोग्रामिंग के 20 वर्षों में, मैंने सचमुच इस शब्द को पहले कभी नहीं सुना है और न ही यह तुरंत स्पष्ट होगा कि इसका क्या मतलब है। मैं जवाब के बारे में शिकायत नहीं कर रहा हूं, बस यह सुझाव दे रहा हूं कि यह शब्द व्यापक रूप से उपयोग / ज्ञात नहीं है। शायद यह सिर्फ मैं ही हूं।
डेरेक एल्किंस

6
कुछ वैश्विक आंकड़े ठीक हैं। इसे "वैश्विक डेटा" कहने के बजाय, आप इसे "पर्यावरण" कह सकते हैं - क्योंकि यही वह है। पर्यावरण में, उदाहरण के लिए, एपीडाटा (विंडोज़ पर) के लिए एक स्ट्रिंग पथ, या मेरे वर्तमान प्रोजेक्ट में सभी घटकों द्वारा उपयोग किए जाने वाले GDI + ब्रश, पेन, फोंट और इसी तरह का पूरा सेट शामिल हो सकता है।
रॉबिन्सन

7
@ रॉबिन्सन काफी नहीं। उदाहरण के लिए, क्या आप वास्तव में चाहते हैं कि आपका छवि लेखन कोड% AppData% को छूने के लिए है, या क्या आप इसके बजाय एक तर्क लेंगे कि कहां लिखना है? यह वैश्विक स्थिति और एक तर्क के बीच का अंतर है। "पर्यावरण" केवल आसानी से एक इंजेक्शन निर्भरता हो सकती है, केवल उन लोगों के लिए मौजूद है जो पर्यावरण के साथ बातचीत के लिए जिम्मेदार हैं। GDI + ब्रश आदि अधिक उचित हैं, लेकिन यह वास्तव में पर्यावरण में संसाधन प्रबंधन का एक मामला है जो आपके लिए ऐसा नहीं कर सकता है - अंतर्निहित एपीआई और / या आपकी भाषा / लाइब्रेरी / रनटाइम की बहुत अधिक कमी।
लुआएन

101

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

उदाहरण के लिए, मान लें कि मुझे दो कैलेंडर तिथियों के बीच दिनों की संख्या की गणना करने की आवश्यकता है, इसलिए मैं एक फ़ंक्शन बनाता हूं:

int daysBetween(Day a, Day b)

ऐसा करने के लिए, मैं फिर एक नया फ़ंक्शन बनाता हूं:

int daysSinceEpoch(Day day)

तब मेरा पहला कार्य बस हो जाता है:

int daysBetween(Day a, Day b)
{
    return daysSinceEpoch(b) - daysSinceEpoch(a);
}

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

जो मैं सुझाऊँगा वह प्रत्येक फ़ंक्शन को देख रहा है और कुछ प्रश्नों के साथ शुरू होगा:

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

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

यदि आपके पास बस बहुत सारे पैरामीटर हैं, तो विधि से ऑब्जेक्ट रिफैक्टिंग पर विचार करें ।


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

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

2
मैं आमतौर पर उस दृष्टिकोण का भी पालन करता हूं, और शायद इस मामले में भी करूंगा। मैं बस इसके आसपास अपनी शब्दावली / शब्दावली में सुधार करना चाहता था ताकि मैं इस पर और अधिक शोध कर सकूं और भविष्य में बेहतर, अधिक केंद्रित प्रश्न कर सकूं।
ecerulm

3
@ecerulm मुझे नहीं लगता कि इसके लिए एक नाम है। यह एक ऐसे लक्षण की तरह है जो बहुत सारी बीमारियों के साथ-साथ गैर-रोग स्थितियों जैसे 'शुष्क मुँह' के लिए आम है। यदि आप कोड की संरचना का वर्णन करते हैं, तो यह कुछ विशिष्ट बात की ओर इशारा करता है।
जिमीजैम

@ecerulm यह बताता है कि कुछ तय होना है - अब यह बहुत अधिक स्पष्ट है कि कुछ तय किया जाना चाहिए था जब यह एक वैश्विक चर था।
इमिविस

61

BobDalgleish ने पहले ही नोट किया है कि इस (विरोधी) पैटर्न को " ट्रम्प डेटा " कहा जाता है ।

मेरे अनुभव में, अत्यधिक ट्रम्प डेटा का सबसे आम कारण लिंक किए गए राज्य चर का एक गुच्छा है जो वास्तव में किसी ऑब्जेक्ट या डेटा संरचना में संक्षिप्त होना चाहिए। कभी-कभी, डेटा को ठीक से व्यवस्थित करने के लिए वस्तुओं का एक गुच्छा घोंसला करना भी आवश्यक हो सकता है।

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

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

playerName = "Bob"
playerEyeColor = GREEN
playerXPosition = -8
playerYPosition = 136
playerHealth = 100
playerMaxHealth = 100

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

mainGameLoop()
 -> processInputEvent()
     -> doPlayerAction()
         -> movePlayer()
             -> checkCollision()
                 -> interactWithNPC()
                     -> interactWithShopkeeper()

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

उचित समाधान है, इस मामले में, पाठ्यक्रम एक खिलाड़ी उद्देश्य यह है कि नाम समाहित परिभाषित करने के लिए, आंखों का रंग, स्थिति, स्वास्थ्य और खिलाड़ी चरित्र के किसी भी अन्य संपत्तियों की है। इस तरह, आपको केवल उन सभी कार्यों के लिए उस एकल ऑब्जेक्ट को पास करना होगा जो किसी भी तरह खिलाड़ी को शामिल करता है।

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

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

इसके बजाय, समाधान के लिए वस्तुओं को किसी भी अन्य वस्तुओं के संदर्भ में स्टोर करना है, जिनके साथ उनके स्थायी या अस्थायी संबंध हैं। इसलिए, उदाहरण के लिए, खिलाड़ी ऑब्जेक्ट (और शायद किसी भी एनपीसी ऑब्जेक्ट्स) को संभवतः "गेम वर्ल्ड" ऑब्जेक्ट के लिए एक संदर्भ स्टोर करना चाहिए, जिसमें वर्तमान स्तर / मैप का संदर्भ होगा, ताकि इस तरह की विधि की player.moveTo(x, y)आवश्यकता न हो स्पष्ट रूप से मानचित्र को एक पैरामीटर के रूप में दिया जाए।

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

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


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

6
@ जिमीजैम: बहुरूपता के बारे में आपकी बात अच्छी है, और मैंने इसे स्वयं बनाने के बारे में सोचा, लेकिन उत्तर को अधिक समय तक रखने के लिए इसे छोड़ दिया। जिस बिंदु को मैं (शायद खराब तरीके से) बनाने की कोशिश कर रहा था, जबकि विशुद्ध रूप से डेटा प्रवाह के संदर्भ में , दोनों के बीच थोड़ा अंतर है foo.method(bar, baz)और method(foo, bar, baz), पूर्व को पसंद करने के लिए (पॉलीमोर्फिज़्म, इनकैप्सुलेशन, लोकलिटी आदि) सहित अन्य कारण हैं।
इल्मरी करोनें

@ इल्मारियारोन: यह भी बहुत स्पष्ट लाभ है कि यह भविष्य में किसी भी भविष्य के परिवर्तन / परिवर्धन / विलोपन / वस्तुओं में रिफैक्टरिंग (जैसे खिलाड़ी) से फ़ंक्शन प्रोटोटाइप का प्रमाण देता है। यह अकेला अमूल्य है।
22

34

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

  • वैश्विक चर के रूप में, यह दायरा बहुत बड़ा है जब कार्यक्रम एक निश्चित आकार तक पहुँच जाता है

  • विशुद्ध रूप से स्थानीय पैरामीटर के रूप में, दायरा बहुत छोटा हो सकता है, जब यह कॉल चेन में बहुत सारे दोहरावदार पैरामीटर सूचियों की ओर जाता है

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


10
उचित वर्ग डिजाइन के लिए +1। यह एक OO समाधान की प्रतीक्षा में एक क्लासिक समस्या की तरह लगता है।
l0b0

21

मेरा मानना ​​है कि जिस पैटर्न का आप वर्णन कर रहे हैं वह वास्तव में निर्भरता इंजेक्शन है । कई टिप्पणीकारों ने तर्क दिया है कि यह एक पैटर्न है , एक विरोधी पैटर्न नहीं है , और मैं सहमत होना चाहूंगा।

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

Sandwich make_sandwich() {
    PeanutButter pb = get_peanut_butter();
    Jelly j = get_jelly();
    return pb + j;
}
extern PhysicalRefrigerator g_refrigerator;
PeanutButter get_peanut_butter() {
    return g_refrigerator.get("peanut butter");
}
Jelly get_jelly() {
    return g_refrigerator.get("jelly");
}

लेकिन यह निर्भरता इंजेक्शन लागू करने और इसके बजाय इसे इस तरह से लिखने के लिए बेहतर अभ्यास होगा :

Sandwich make_sandwich(Refrigerator& r) {
    PeanutButter pb = get_peanut_butter(r);
    Jelly j = get_jelly(r);
    return pb + j;
}
PeanutButter get_peanut_butter(Refrigerator& r) {
    return r.get("peanut butter");
}
Jelly get_jelly(Refrigerator& r) {
    return r.get("jelly");
}

अब आपके पास एक फ़ंक्शन है जो स्पष्ट रूप से अपने फ़ंक्शन हस्ताक्षर में अपनी सभी निर्भरता को दस्तावेज़ित करता है, जो पठनीयता के लिए महान है। आखिरकार, यह सच है कि make_sandwichआपको एक तक पहुँचने की आवश्यकता है Refrigerator; इसलिए पुराने फ़ंक्शन हस्ताक्षर मूल रूप से रेफ्रिजरेटर को अपने इनपुट के भाग के रूप में नहीं लेने से असंतुष्ट थे।

एक बोनस के रूप में, यदि आप अपनी कक्षा पदानुक्रम को सही करते हैं, तो स्लाइसिंग से बचें, और इसी तरह, आप make_sandwichएक में पास होकर फ़ंक्शन का परीक्षण भी कर सकते हैं MockRefrigerator! (आपको इसे इस तरह इकाई-परीक्षण करने की आवश्यकता हो सकती है क्योंकि आपके इकाई-परीक्षण वातावरण में किसी भी तक पहुँच नहीं हो सकती है PhysicalRefrigerator।)

मुझे यह समझ में नहीं आता है कि निर्भरता इंजेक्शन के सभी उपयोगों के लिए कॉल स्टैक के नीचे कई स्तरों पर समान रूप से नामित पैरामीटर की आवश्यकता होती है , इसलिए मैं आपके द्वारा पूछे गए प्रश्न का सही उत्तर नहीं दे रहा हूं ... लेकिन यदि आप इस विषय पर आगे पढ़ने की तलाश कर रहे हैं, "निर्भरता इंजेक्शन" निश्चित रूप से आपके लिए एक प्रासंगिक कीवर्ड है।


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

8
@DewiMorgan: जाहिर है कि आप Refrigeratorएक में सामान्यीकरण करने के लिए आगे रिफ्लेक्टर कर सकते हैं IngredientSource, या "सैंडविच" की धारणा को भी सामान्य कर सकते हैं template<typename... Fillings> StackedElementConstruction<Fillings...> make_sandwich(ElementSource&); जिसे "सामान्य प्रोग्रामिंग" कहा जाता है और यह यथोचित शक्तिशाली है, लेकिन निश्चित रूप से यह ओपी की तुलना में अधिक रहस्यमय है जो वास्तव में अभी प्राप्त करना चाहता है। सैंडविच कार्यक्रमों के लिए उचित स्तर की अमूर्तता के बारे में एक नया प्रश्न खोलने के लिए स्वतंत्र महसूस करें। ;)
क्क्सप्लसोन

11
कोई गलती न हो, अप्रभावित उपयोगकर्ता तक पहुंच नहीं होनी चाहिए make_sandwich()
डॉटान्चेन

2
@ डिव्यू - एक्सकेसीडी लिंक
गेविन लॉक

19
आपके कोड में सबसे गंभीर त्रुटि यह है कि आप रेफ्रिजरेटर में मूंगफली का मक्खन रख रहे हैं।
मालवोलियो

15

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


3
यदि level3()आवश्यक हो newParam, तो यह सुनिश्चित करने के लिए युग्मन है, लेकिन किसी तरह कोड के विभिन्न हिस्सों को एक दूसरे के साथ संवाद करना होगा। यदि फ़ंक्शन उस पैरामीटर का उपयोग करता है, तो मुझे आवश्यक रूप से फ़ंक्शन पैरामीटर खराब युग्मन नहीं कहेंगे। मुझे लगता है कि श्रृंखला का समस्याग्रस्त पहलू इसके लिए शुरू किया गया अतिरिक्त युग्मन है level1()और इसे पास करने level2()के newParamअलावा इसका कोई उपयोग नहीं है । अच्छा जवाब, युग्मन के लिए +1।
अशक्त

6
@ यदि वे वास्तव में इसके लिए उपयोग नहीं करते हैं, तो वे अपने कॉलर से प्राप्त करने के बजाय एक मूल्य बना सकते हैं।
रैंडम 832

3

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

मैं उन जवाबों से सहमत हूं जो इस समस्या से बचने के लिए कुछ और करने का सुझाव देते हैं। हालाँकि, एक और तरीका यह भी है कि आप बिना किसी तर्क के इस तर्क को गहराई से कम करने में मदद करें " बस एक क्लास पास करें जहाँ आप कई तर्क देते थे! " एक। उदाहरण के लिए, यहाँ कुछ कोड से पहले है :

public void PerformReporting(StuffRepository repo, string desiredName) {
   var stuffs = repo.GetStuff(DateTime.Now());
   FilterAndReportStuff(stuffs, desiredName);
}

public void FilterAndReportStuff(IEnumerable<Stuff> stuffs, string desiredName) {
   var filter = CreateStuffFilter(FilterTypes.Name, desiredName);
   ReportStuff(stuffs.Filter(filter));
}

public void ReportStuff(IEnumerable<Stuff> stuffs) {
   stuffs.Report();
}

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

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

public void PerformReporting(StuffRepository repo, string desiredName) {
   var stuffs = repo.GetStuff(DateTime.Now());
   var filter = CreateStuffFilter(FilterTypes.Name, desiredName);
   var filteredStuffs = stuffs.Filter(filter)
   filteredStuffs.Report();
}

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

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

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

मैंने हाल ही में इकाई परीक्षणों को एक वर्ग में जोड़ने की कोशिश की (जो मैंने नहीं लिखा) जिसमें 17 निर्भरताएं जैसे कुछ लिया गया था, जिनमें से सभी का मजाक उड़ाया गया था! मैंने इसे अभी तक पूरा नहीं किया है, लेकिन मैंने कक्षा को तीन वर्गों में विभाजित किया है, प्रत्येक एक अलग संज्ञाओं के साथ काम कर रहा है जो इसके साथ संबंध था, और सबसे खराब एक के लिए निर्भरता सूची 12 तक पहुंच गई और उपस्थिति के लिए लगभग 8 सर्वश्रेष्ठ।

परीक्षणशीलता आपको बेहतर कोड लिखने के लिए मजबूर करेगी । आपको यूनिट टेस्ट लिखना चाहिए क्योंकि आप पाएंगे कि यह आपको अपने कोड के बारे में अलग तरह से सोचने देता है और आप यूनिट टेस्ट लिखने से पहले कितने बग्स से बच सकते हैं, इसके लिए आप गेट-गो से बेहतर कोड लिखेंगे।


2

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


1
विवरणों पर थोड़ा कमजोर है, जो संभवतः डाउनवोट्स की व्याख्या करता है। फिर भी आत्मा में यह उत्तर बिल्कुल हाजिर है: ओपी को कानून के कानून पर पढ़ना चाहिए - यह प्रासंगिक शब्द है।
कोनराड रुडोल्फ

4
FWIW, मुझे नहीं लगता कि लॉ ऑफ डेमेटर (उर्फ "कम से कम विशेषाधिकार") प्रासंगिक है। ओपी का मामला यह है कि उसका कार्य यदि अपना डेटा नहीं कर पाएगा, तो उसके पास ट्रैम्प डेटा नहीं था (क्योंकि कॉल स्टैक के अगले आदमी को इसकी आवश्यकता है, क्योंकि अगले आदमी को इसकी आवश्यकता है, और इसी तरह)। कम से कम विशेषाधिकार / कानून का कानून केवल तभी प्रासंगिक है जब पैरामीटर वास्तव में अप्रयुक्त है , और उस स्थिति में यह निश्चित है: अप्रयुक्त पैरामीटर को हटा दें!
Quuxplusone

2
इस प्रश्न की स्थिति का कानून के कानून से कोई लेना-देना नहीं है ... विधि कॉल की श्रृंखला के बारे में एक सतही समानता है, लेकिन अन्यथा यह बहुत अलग है।
एरिक किंग

@Quuxplusone संभव है, हालांकि इस मामले में विवरण काफी भ्रामक है क्योंकि जंजीर कॉल वास्तव में उस परिदृश्य में कोई मतलब नहीं रखते हैं: उन्हें इसके बजाय नेस्टेड किया जाना चाहिए ।
कोनराड रूडोल्फ

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

1

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

लेकिन, ओपी द्वारा कहा गया दृष्टिकोण बहुत उपयोगी है यदि कोई व्यक्ति कार्यात्मक प्रोग्रामिंग कर रहा है।


0

यहां कोई भी विरोधी पैटर्न नहीं है, क्योंकि कॉलर को इन सभी स्तरों के बारे में पता नहीं है और परवाह नहीं है।

कोई व्यक्ति हाईलेवल (params) को बुला रहा है और यह अपेक्षा करता है कि हाईलेवल को अपना काम करना चाहिए। पैरलल के साथ हाईलेवल क्या करता है, कॉल करने वालों का कोई भी व्यवसाय नहीं है। highLevel समस्या को सबसे अच्छे तरीके से संभालता है, इस मामले में params to level1 (params) पास करता है। यह बिल्कुल ठीक है।

आप एक कॉल चेन देखते हैं - लेकिन कॉल चेन नहीं है। शीर्ष पर एक कार्य है जो अपना काम सबसे अच्छा तरीका है जो वह कर सकता है। और अन्य कार्य हैं। प्रत्येक फ़ंक्शन को किसी भी समय बदला जा सकता है।

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