विधि बनाम अतिक्रमण


17

"सिंगल-एक्सेस-पॉइंट" विधियों के आधार पर विधि की क्लासिक OOP समस्या है:

main.getA().getB().getC().transmogrify(x, y)

बनाम

main.getA().transmogrifyMyC(x, y)

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

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

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

जवाबों:


25

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

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

लॉ ऑफ़ डेमेटर का एक नुकसान यह है कि कभी-कभी घटकों को विधि कॉल को प्रचारित करने के लिए बड़ी संख्या में छोटे "रैपर" तरीकों को लिखने की आवश्यकता होती है। इसके अलावा, एक वर्ग का इंटरफ़ेस भारी हो सकता है क्योंकि इसमें सम्‍मिलित वर्गों के लिए तरीके हो सकते हैं, जिसके परिणामस्वरूप एक वर्ग बिना कोसिव इंटरफ़ेस के होता है। लेकिन यह खराब ओओ डिजाइन का संकेत भी हो सकता है।


मैं उस कानून के बारे में भूल गया, मुझे याद दिलाने के लिए धन्यवाद। लेकिन मैं यहां जो कुछ भी पूछ रहा हूं, वह मुख्य रूप से क्या फायदे और नुकसान हैं, या अधिक सटीक रूप से मुझे एक शैली को दूसरे पर उपयोग करने का निर्णय कैसे लेना चाहिए।
ओक

@ ओक, मैंने फायदे और नुकसान का वर्णन करते हुए उद्धरण जोड़े।
पेटर टॉर्क

10

मैं सामान्य रूप से विधि को यथासंभव सीमित रखने की कोशिश करता हूं ( कानून के कानून के आधार पर )

एकमात्र अपवाद मैं धाराप्रवाह इंटरफेस / आंतरिक डीएसएल शैली प्रोग्रामिंग के लिए है।

मार्टिन फॉवलर डोमेन-विशिष्ट-भाषाओं में समान अंतर का वर्णन करता है, लेकिन उल्लंघन कमांड क्वेरी अलगाव के कारणों के लिए जो बताता है:

प्रत्येक विधि या तो एक कमांड होनी चाहिए जो एक क्रिया करती है, या एक क्वेरी जो कॉलर को डेटा लौटाती है, लेकिन दोनों नहीं।

फाउलर अपनी पुस्तक में पेज 70 पर कहते हैं:

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


3

मुझे लगता है कि सवाल यह है कि क्या आप एक उपयुक्त अमूर्त का उपयोग कर रहे हैं।

पहले मामले में, हमारे पास है

interface IHasGetA {
    IHasGetB getA();
}

interface IHasGetB {
    IHasGetC getB();
}

interface IHasGetC {
    ITransmogrifyable getC();
}

interface ITransmogrifyable {
    void transmogrify(x,y);
}

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

main.getA(v).getB(w).getC(x).transmogrify(y, z);

से अक्सर बेहतर होता है

main.superTransmogrify(v, w, x, y, z);

क्योंकि बाद उदाहरण में दोनों thisऔर mainके प्रकार पर निर्भर करते हैं v, w, x, yऔर z। इसके अलावा, कोड वास्तव में बहुत बेहतर नहीं दिखता है, अगर हर विधि घोषणा में आधा दर्जन तर्क हैं।

सेवा लोकेटर को वास्तव में पहले दृष्टिकोण की आवश्यकता होती है। आप सेवा लोकेटर के माध्यम से बनाए जाने वाले उदाहरण तक पहुँच प्राप्त नहीं करना चाहते हैं।

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

उदाहरण के लिए, आप कर सकते हैं:

class Main implements IHasGetA, IHasGetA, IHasGetA, ITransmogrifyable {
    IHasGetB getA() { return this; }
    IHasGetC getB() { return this; }
    ITransmogrifyable getC() { return this; }
    void transmogrify(x,y) {
        return x + y;//yeah!
    }
}

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


मापदंडों की संख्या में बड़ी वृद्धि के बारे में बहुत दिलचस्प बिंदु।
ओक

2

Demeter के कानून , के रूप में @ Péter Török बताते हैं, "कॉम्पैक्ट" फ़ॉर्म पता चलता है।

इसके अलावा, अधिक विधियाँ जो आप स्पष्ट रूप से अपने कोड में उल्लेख करते हैं, अधिक वर्ग जो आपकी कक्षा पर निर्भर करते हैं, रखरखाव के मुद्दों को बढ़ाते हैं। आपके उदाहरण में, कॉम्पैक्ट रूप दो वर्गों पर निर्भर करता है, जबकि लंबा रूप चार वर्गों पर निर्भर करता है। लंबे समय तक फॉर्म न केवल कानून के कानून का उल्लंघन करता है; यह आपको हर बार जब आप चार संदर्भित तरीकों में से किसी को भी बदलते हैं (जैसा कि कॉम्पैक्ट रूप में दो के विपरीत होता है) अपना कोड बदल देगा।


दूसरी ओर आँख बंद करके उस कानून का मतलब है कि तरीकों की संख्या में Aविस्फोट Aहो सकता है बहुत सारे तरीके वैसे भी दूर सौंपना चाहते हैं। फिर भी, मैं निर्भरताओं से सहमत हूं - जो क्लाइंट कोड से आवश्यक निर्भरता की मात्रा को काफी कम करता है।
ओक

1
@Oak: किसी भी चीज़ को आँख बंद करके करना कभी भी अच्छा नहीं होता है। पेशेवरों और विपक्ष को देखने और सबूत के आधार पर निर्णय लेने की आवश्यकता है। इसमें लॉ ऑफ़ डेमेटर भी शामिल है।
सेसरगॉन

2

मैं खुद इस समस्या से लड़ चुका हूँ। अलग-अलग वस्तुओं में "पहुँचने" के साथ नकारात्मक पक्ष यह है कि जब आप रीफैक्टरिंग करते हैं तो आप एक बहुत सारे कोड बदलने के लिए बहुत सारे कोड बदल देंगे। साथ ही, आपका कोड थोड़ा फूला हुआ और पढ़ने में कठिन हो जाता है।

दूसरी ओर, कक्षाएं जो केवल "पास" विधियों के साथ होती हैं, का अर्थ है एक से अधिक स्थानों पर एक से अधिक विधियों की घोषणा करने का ओवरहेड होना।

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


1

मुझे अक्सर लगता है कि एक कार्यक्रम का तर्क जंजीर विधियों के साथ करना आसान है। मेरे लिए, customer.getLastInvoice().itemCount()मेरे दिमाग में बेहतर से फिट बैठता है customer.countLastInvoiceItems()

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


वह IMO ग्राहक होना चाहिए ।NrLastInvoices या customer.LastInvoice.NrItems। यह श्रृंखला बहुत लंबी नहीं है, इसलिए यदि यह संयोजन की मात्रा कुछ बड़ी है तो शायद यह मूल्य नहीं है
Homde
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.