क्या विशेष मामलों में कमियां लिस्कॉव प्रतिस्थापन सिद्धांत का उल्लंघन करती हैं?


20

मान लें कि मेरे पास एक इंटरफ़ेस FooInterfaceहै जिसमें निम्नलिखित हस्ताक्षर हैं:

interface FooInterface {
    public function doSomething(SomethingInterface something);
}

और एक ठोस वर्ग ConcreteFooजो उस इंटरफ़ेस को लागू करता है:

class ConcreteFoo implements FooInterface {

    public function doSomething(SomethingInterface something) {
    }

}

ConcreteFoo::doSomething()अगर कुछ विशेष प्रकार की SomethingInterfaceवस्तु (यह कहते हैं कि यह कहा जाता है SpecialSomething) पारित कर दिया है, तो मैं कुछ अनूठा करना चाहता हूं ।

यह निश्चित रूप से एलएसपी उल्लंघन है अगर मैं विधि की पूर्व शर्त को मजबूत करता हूं या एक नया अपवाद फेंकता हूं, लेकिन क्या यह अभी भी एलएसपी उल्लंघन होगा यदि मैं SpecialSomethingजेनेरिक SomethingInterfaceऑब्जेक्ट के लिए कमबैक प्रदान करते समय विशेष-कैसड ऑब्जेक्ट? कुछ इस तरह:

class ConcreteFoo implements FooInterface {

    public function doSomething(SomethingInterface something) {
        if (something instanceof SpecialSomething) {
            // Do SpecialSomething magic
        }
        else {
            // Do generic SomethingInterface magic
        }
    }

}

जवाबों:


19

यह एलएसपी का उल्लंघन हो सकता है जो इस बात पर निर्भर करता है कि doSomethingविधि के लिए अनुबंध में और क्या है । लेकिन यह लगभग निश्चित रूप से एक कोड गंध है भले ही यह एलएसपी का उल्लंघन नहीं करता है।

उदाहरण के लिए, यदि का अनुबंध का हिस्सा doSomethingहै कि यह कॉल करेंगे है something.commitUpdates()एक बार लौटने से पहले कम से कम, और विशेष मामले के लिए यह कॉल commitSpecialUpdates()के बजाय, तो वह है LSP का उल्लंघन। यहां तक कि अगर SpecialSomethingकी commitSpecialUpdates()विधि बूझकर रूप में एक ही सामान के सभी करने के लिए डिजाइन किया गया था commitUpdates(), यह सिर्फ preemptively LSP उल्लंघन के आसपास हैकिंग है, और है कि वास्तव में hackery एक की तरह अगर एक LSP लगातार पीछा करने की जरूरत नहीं होगी। आपके मामले में ऐसा कुछ भी लागू होता है या नहीं, यह आपको उस विधि के लिए अपने अनुबंध की जाँच करके पता लगाना होगा (चाहे स्पष्ट या निहित हो)।

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


धन्यवाद! इससे मदद मिलती है। हाइपोथेटिक रूप से, doSomething()विधि का उद्देश्य टाइप रूपांतरण होगा SpecialSomething: यदि यह प्राप्त करता है, तो यह SpecialSomethingकेवल वस्तु को अनमॉडिफाइड लौटाएगा, जबकि यदि यह एक सामान्य SomethingInterfaceवस्तु प्राप्त करता है, तो यह ऑब्जेक्ट में बदलने के लिए एक एल्गोरिथ्म चलाएगा SpecialSomething। चूँकि पूर्वधारणाएँ और पदबंध समान हैं, इसलिए मुझे नहीं लगता कि अनुबंध का उल्लंघन किया गया है।
इवान

1
@ इवान ओह वाह ... यह एक दिलचस्प मामला है। यह वास्तव में पूरी तरह से अप्रमाणिक हो सकता है। केवल एक चीज जो मैं सोच सकता हूं, वह यह है कि यदि आप किसी नई वस्तु का निर्माण करने के बजाय मौजूदा वस्तु को वापस कर रहे हैं, तो हो सकता है कि कोई व्यक्ति इस विधि पर निर्भर करता है कि वह किसी नई वस्तु को लौटाए ... लेकिन क्या उस तरह की चीज लोगों को तोड़ सकती है, इस पर निर्भर हो सकती है भाषा। क्या किसी के लिए कॉल करना संभव है y = doSomething(x), फिर x.setFoo(3), और फिर यह पता लगाएं कि y.getFoo()रिटर्न 3?
Ixrec

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

1

यह एलएसपी का उल्लंघन नहीं है। हालाँकि, यह अभी भी "टाइप चेक न करें" नियम का उल्लंघन है। कोड को इस तरह से डिजाइन करना बेहतर होगा कि विशेष स्वाभाविक रूप से उत्पन्न हो; शायद SomethingInterfaceएक और सदस्य की जरूरत है जो इसे पूरा कर सके, या हो सकता है कि आपको कहीं एक अमूर्त कारखाने को इंजेक्ट करने की आवश्यकता हो।

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


1

नहीं, इस तथ्य का लाभ उठाते हुए कि एक दिया गया तर्क केवल इंटरफ़ेस A प्रदान नहीं करता है, लेकिन A2 भी, एलएसपी का उल्लंघन नहीं करता है।

बस यह सुनिश्चित करें कि विशेष पथ के पास कोई मजबूत पूर्व-स्थितियां नहीं हैं (इसे लेने के लिए परीक्षण करने वालों से अलग), और न ही कोई कमजोर स्थिति।

C ++ टेम्पलेट अक्सर बेहतर प्रदर्शन प्रदान करने के लिए ऐसा करते हैं, उदाहरण के लिए InputIterators की आवश्यकता होती है , लेकिन यदि RandomAccessIterators के साथ बुलाया जाता है तो अतिरिक्त गारंटी देता है ।

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

विशेष मामले का लाभ उठाते हुए अक्सर, सूखी (अपने आप को दोहराने नहीं है) के खिलाफ जाता है के रूप में आप कोड नकल करना पड़ सकता है, और KISS के खिलाफ (यह सरल रखें), के रूप में यह और अधिक जटिल है।


0

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

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


0

लिस्कोव प्रतिस्थापन सिद्धांत उनके सुपरपाइप के एक अनुबंध के अनुसार अभिनय करने वाले उपप्रकारों के बारे में है। इसलिए, जैसा कि Ixrec ने लिखा है, यह जवाब देने के लिए पर्याप्त जानकारी नहीं है कि क्या यह एलएसपी उल्लंघन है।

यहाँ एक खुला बंद सिद्धांत है, जिसका उल्लंघन किया जाता है। यदि आपके पास कोई नई आवश्यकता है - Do SpecialSomething जादू - और आपको मौजूदा कोड को संशोधित करना होगा, तो आप निश्चित रूप से OCP का उल्लंघन कर रहे हैं ।

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