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


60

मैं जावा में प्रोग्रामिंग कर रहा हूं, और मैं हमेशा कन्वर्टर्स को इस तरह बनाता हूं:

public OtherObject MyObject2OtherObject(MyObject mo){
    ... Do the conversion
    return otherObject;
}

नए कार्यस्थल पर पैटर्न है:

public void MyObject2OtherObject(MyObject mo, OtherObject oo){
    ... Do the conversion
}

मेरे लिए यह थोड़ा बदबूदार है, क्योंकि मुझे आने वाले मापदंडों को नहीं बदलने की आदत है। क्या यह आने वाला पैरामीटर परिवर्तन एक एंटीपैटर्न है या क्या यह ठीक है? क्या इसमें कुछ गंभीर कमियां हैं?


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

1
दूसरा पैटर्न कभी-कभी वस्तुओं के पुन: उपयोग के लिए एक दक्षता उपाय के रूप में उपयोग किया जाता है। मान लें कि ooएक ऑब्जेक्ट है जो विधि को पारित किया गया है, न कि एक संकेतक जो एक नई वस्तु पर सेट है। क्या यहाँ भी ऐसा ही है? अगर यह जावा है तो शायद यह है, अगर इसका सी ++ यह नहीं हो सकता है
रिचर्ड टिंगल

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

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

12
मैं इन दोनों अलग-अलग पैटर्नों को उसी तरह लागू करने वाले कार्यों का नाम नहीं दूंगा।
केसी

जवाबों:


70

यह एक एंटीपैटर्न नहीं है, यह एक बुरा अभ्यास है।

एक एंटीपैटर्न और एक मात्र बुरे अभ्यास के बीच का अंतर यहाँ है: पैटर्न-विरोधी परिभाषा

अंकल बॉब के क्लीन कोड के अनुसार, आपके द्वारा प्रदर्शित की जाने वाली नई कार्यशैली एक खराब प्रथा , वैश्यावृत्ति या पूर्व-ओओपी समय है।

किसी फ़ंक्शन के इनपुट के रूप में तर्क सबसे स्वाभाविक रूप से व्याख्या किए जाते हैं।

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


7
मैं यह नहीं देखता कि जिस परिभाषा से आप जुड़े हैं, उसे एक प्रतिमान क्यों माना जा रहा है।
शमूएल एडविन वार्ड

7
मुझे लगता है कि क्योंकि यह तर्क है कि "हम नई वस्तु नहीं बनाकर सीपीयू / मेम की बचत कर रहे हैं, इसके बजाय हमें एक हमारे पास रीसायकल करना चाहिए और इसे एक arg के रूप में पारित करना चाहिए" इसलिए स्पष्ट रूप से गंभीर OOP पृष्ठभूमि वाले हर किसी के लिए यह गलत है कि यह हो सकता है कभी भी एक पैटर्न का गठन न करें - इसलिए कोई तरीका नहीं है कि हम इसे परिभाषा द्वारा एक एंटी-पैटर्न
मान सकें

1
उस मामले के बारे में क्या है जब एक इनपुट पैरामीटर ऑब्जेक्ट का एक बड़ा संग्रह है जिसे संशोधित करने की आवश्यकता है?
स्टीव चैम्बर्स

हालांकि मैं भी विकल्प 1 पसंद करता हूं, अगर OtherObjectकोई इंटरफ़ेस है तो क्या होगा ? केवल कॉलर (उम्मीद है) जानता है कि वह किस प्रकार का कंक्रीट चाहता है।
user949300

@SteveChambers मुझे लगता है, इस मामले में, वर्ग या विधि का डिज़ाइन खराब है और इसे से बचने के लिए फिर से भरना चाहिए।
piotr.wittchen

16

रॉबर्ट सी। मार्टिन की प्रसिद्ध पुस्तक "क्लीन कोड" का उद्धरण:

आउटपुट तर्क से बचा जाना चाहिए

फ़ंक्शंस में कम संख्या में तर्क होने चाहिए

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


1
मैं कहूंगा कि यह पहले एक का उल्लंघन करता है, बहुत सारे तर्कों का मतलब गड़बड़ कोड है, लेकिन मुझे लगता है कि दो तर्क पूरी तरह से ठीक हैं। मुझे लगता है कि स्वच्छ कोड पर नियम का मतलब है कि यदि आपको 5 या 6 आने वाले डेटा की आवश्यकता है, तो आप एक ही विधि में बहुत अधिक करना चाहते हैं, इसलिए आपको कोड पठनीयता और ओओपी गुंजाइश बढ़ाने के लिए रिफ्लेक्टर करना चाहिए।
CsBalazsHungary

2
वैसे भी मुझे संदेह है कि यह एक सख्त कानून नहीं है, बल्कि एक मजबूत सुझाव है। मुझे पता है कि यह आदिम प्रकारों के साथ काम नहीं करेगा, अगर यह एक परियोजना में एक विस्तृत प्रसार पैटर्न है तो एक जूनियर को एक इंट पास करने के लिए विचार मिलेगा और यह ऑब्जेक्ट की तरह बदलने की उम्मीद करेगा।
CsBalazsHungary

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

3
ठीक है, नरक, अगर किसी व्यक्ति ने इसे एक किताब में लिखा है तो यह किसी भी बोधगम्य स्थिति के लिए अच्छी सलाह होना चाहिए। विचार की कोई जरूरत नहीं।
केसी

2
@emodendroket लेकिन यार, यह पुरुष!
पियरे अरलाउड

15

दूसरा मुहावरा अधिक तेज़ हो सकता है, क्योंकि कॉल करने वाला प्रत्येक लूप पर एक चर का पुन: उपयोग कर सकता है, इसके बजाय प्रत्येक पुनरावृत्ति को नया उदाहरण बनाता है।

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


12
ठीक है, मैं सहमत हो सकता हूं कि क्या यह अड़चन है, लेकिन मैंने जहां भी काम किया, अड़चनें आमतौर पर कम दक्षता वाले एल्गोरिदम हैं, न कि क्लोनिंग या ऑब्जेक्ट बनाने के लिए। मैं खेल के विकास को कम जानता हूं।
CsBalazsHungary

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

JMonkey भी है जहाँ मैंने इसे बहुत देखा है क्योंकि यह अक्सर प्रदर्शन क्रिटिकल होता है। मैंने कभी इसे "जावामोंकी" नहीं सुना, हालांकि
रिचर्ड

3
@ जेएबी: जेवीएम की जीसी विशेष रूप से पंचांग वस्तुओं के मामले के लिए बनाई गई है। बहुत कम मात्रा में जीवित वस्तुओं को इकट्ठा करने के लिए तुच्छ हैं, और कई मामलों में आपकी संपूर्ण पंचांग पीढ़ी को एकल सूचक चाल के साथ एकत्र किया जा सकता है।
फोसि

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

10

मुझे नहीं लगता कि कोड के दो बराबर टुकड़े हैं। पहले मामले में, आपको बनाना होगा otherObject। आप दूसरे में मौजूदा उदाहरण को संशोधित कर सकते हैं। दोनों का यह उपयोग है। कोड की गंध एक दूसरे पर पसंद कर रही होगी।


मुझे पता है कि वे समकक्ष नहीं हैं। आप सही हैं, हम इन चीजों को जारी रख रहे हैं, इसलिए इससे फर्क पड़ेगा। तो आप कह रहे हैं कि उनमें से कोई भी एंटीपैटर्न नहीं है, यह केवल उपयोग के मामले का सवाल है?
CsBalazsHungary

@CBalazsHungary मैं हाँ कहूँगा। आपके द्वारा प्रदान किए गए कोड के छोटे टुकड़े को देखते हुए।
व्यंग्यात्मक

मुझे लगता है कि यह अधिक गंभीर मुद्दा बन जाता है यदि आपके पास एक आने वाला आदिम पैरामीटर है, तो निश्चित रूप से अपडेट नहीं किया जाएगा, इसलिए @claasz ने लिखा: इसे तब तक टाला जाना चाहिए जब तक कि यह परिशिष्ट न हो।
CsBalazsHungary

1
@CBalazsHungary आदिम तर्क के मामले में, फ़ंक्शन भी काम नहीं करेगा। इसलिए आपको तर्क बदलने से ज्यादा समस्या है।
व्यंग्यात्मक

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

7

यह वास्तव में भाषा पर निर्भर करता है।

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

यह एक inपैरामीटर को संशोधित करने या एक पैरामीटर को पढ़ने के लिए एक त्रुटि है out, लेकिन एक पैरामीटर में कोई भी परिवर्तन in outकॉलर को वापस पारित किया जाता है।

तो Ada में दो रूप (ये भी कानूनी VHDL हैं)

function MyObject2OtherObject(mo : in MyObject) return OtherObject is
begin
    ... Do the conversion
    return otherObject;
end MyObject2OtherObject;

तथा

procedure MyObject2OtherObject(mo : in MyObject; oo : out OtherObject) is
begin
    ... Do the conversion
    oo := ... the conversion result;
end MyObject2OtherObject;

दोनों के अपने उपयोग हैं; एक प्रक्रिया कई Outमापदंडों में कई मान लौटा सकती है जबकि एक फ़ंक्शन केवल एक परिणाम दे सकता है। क्योंकि दूसरे पैरामीटर का उद्देश्य प्रक्रिया घोषणा में स्पष्ट रूप से कहा गया है, इस फॉर्म पर कोई आपत्ति नहीं हो सकती है। मैं पठनीयता के लिए फ़ंक्शन को प्राथमिकता देता हूं। लेकिन ऐसे मामले होंगे जहां प्रक्रिया बेहतर है, उदाहरण के लिए जहां कॉलर ने पहले ही ऑब्जेक्ट बनाया है।


3

यह वास्तव में बदबूदार लगता है, और अधिक संदर्भ देखे बिना यह सुनिश्चित करना असंभव है। ऐसा करने के दो कारण हो सकते हैं, हालांकि दोनों के लिए विकल्प हैं।

सबसे पहले, यह आंशिक रूपांतरण लागू करने, या रूपांतरण विफल होने पर डिफ़ॉल्ट मानों के साथ परिणाम छोड़ने का एक संक्षिप्त तरीका है। अर्थात्, आपके पास यह हो सकता है:

public void ConvertFoo(Foo from, Foo to) {
    if (can't convert) {
        return;
    }
    ...
}

Foo a;
Foo b = DefaultFoo();
ConvertFoo(a, b);
// If conversion fails, b is unchanged

बेशक, आमतौर पर यह अपवादों का उपयोग करके नियंत्रित किया जाता है। हालांकि, भले ही अपवादों को किसी भी कारण से बचने की आवश्यकता हो, लेकिन ऐसा करने का एक बेहतर तरीका है - TryParse पैटर्न एक विकल्प।

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

जावा कई आउटपुट से निपटने में महान नहीं है - इसमें कुछ भाषाओं की तरह केवल आउटपुट पैरामीटर नहीं हो सकते हैं, या अन्य की तरह कई रिटर्न मान हो सकते हैं - लेकिन फिर भी, आप रिटर्न ऑब्जेक्ट का उपयोग कर सकते हैं।

निरंतरता कारण बल्कि लंगड़ा है लेकिन दुख की बात है कि यह सबसे आम हो सकता है।

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

2

दूसरे पैटर्न का लाभ यह है कि यह कॉल करने वाले को निर्मित वस्तु के लिए स्वामित्व और जिम्मेदारी लेने के लिए मजबूर करता है। कोई सवाल नहीं है कि क्या विधि ने वस्तु बनाई या नहीं और इसे पुन: प्रयोज्य पूल से प्राप्त किया गया है या नहीं। फोन करने वाला जानता है कि वे जीवन भर और नई वस्तु के निपटान के लिए जिम्मेदार हैं।

इस विधि के नुकसान हैं:

  1. फ़ैक्टरी विधि के रूप में उपयोग नहीं किया जा सकता है, फोन करने वाले को OtherObjectइसकी ज़रूरतों का सटीक उपप्रकार पता होना चाहिए और इसका निर्माण अग्रिम में करना चाहिए।
  2. उन वस्तुओं के साथ उपयोग नहीं किया जा सकता है जिनके निर्माण में पैरामीटर की आवश्यकता होती है यदि वे पैरामीटर आते हैं MyObjectOtherObjectके बारे में जानने के बिना रचनात्मक होना चाहिए MyObject

उत्तर c # में मेरे अनुभव पर आधारित है, मुझे आशा है कि तर्क जावा में अनुवाद करता है।


मुझे लगता है कि आप जिस जिम्मेदारी का उल्लेख करते हैं, वह वस्तुओं के जीवन चक्र को "देखने में कठिन" बनाता है। एक जटिल विधि प्रणाली में हम किसी वस्तु के प्रत्यक्ष संशोधन को पसंद करेंगे, बजाय इसके कि हम जिस भी विधि से गुजर रहे हैं, उसके माध्यम से देखें।
CsBalazsHungary

मैं यही सोच रहा था। निर्भरता इंजेक्शन
Lyndon व्हाइट

अन्य लाभ यह है कि OtherObject सार या इंटरफ़ेस है। फ़ंक्शन का कोई पता नहीं है कि किस प्रकार का निर्माण करना है लेकिन कॉलर हो सकता है।
user949300

2

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

हालाँकि, अगर विधि का नाम था Convert(जो यह होना चाहिए अगर हम "... रूपांतरण करते हैं" भाग को देखें), तो मैं कहूंगा कि दूसरी विधि का कोई मतलब नहीं होगा। आप किसी अन्य मान में पहले से मौजूद मान को IMO में परिवर्तित नहीं करते हैं।

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