Liskov प्रतिस्थापन सिद्धांत का एक उदाहरण क्या है?


908

मैंने सुना है कि लिसकोव प्रतिस्थापन सिद्धांत (एलएसपी) वस्तु उन्मुख डिजाइन का एक मूल सिद्धांत है। यह क्या है और इसके उपयोग के कुछ उदाहरण क्या हैं?


एलएसपी पालन और उल्लंघन के और अधिक उदाहरण यहां दिए गए हैं
स्टुअर्टएलसी

1
इस प्रश्न के अनंत रूप से कई अच्छे उत्तर हैं और इसलिए यह बहुत व्यापक है
राएडवल्ड

जवाबों:


892

एक बढ़िया उदाहरण एलएसपी (उदाहरण में मैंने हाल ही में सुना है पोडकास्ट में अंकल बॉब द्वारा दिया गया) था कि कैसे कभी-कभी कुछ ऐसा होता है जो प्राकृतिक भाषा में सही लगता है, कोड में काफी काम नहीं करता है।

गणित में, a Squareएक है Rectangle। वास्तव में यह एक आयत की विशेषज्ञता है। "एक है" आपको यह विरासत के साथ मॉडल करना चाहता है। हालाँकि, यदि कोड में आप से Squareव्युत्पन्न किया गया है Rectangle, तो एक Squareउम्मीद के मुताबिक होना चाहिए कहीं भी आप एक उम्मीद है Rectangle। यह कुछ अजीब व्यवहार के लिए बनाता है।

कल्पना कीजिए कि आपके पास SetWidthऔर SetHeightआपके Rectangleआधार वर्ग के तरीके थे ; यह पूरी तरह से तर्कसंगत लगता है। लेकिन अगर आपके Rectangleसंदर्भ एक की ओर इशारा किया Square, तो SetWidthऔर SetHeightकोई मतलब नहीं है क्योंकि एक की स्थापना अन्य इससे मिलते हुए बदल जाएगा। इस मामले में के Squareसाथ Liskov प्रतिस्थापन परीक्षण विफल रहता है Rectangleऔर Squareसे विरासत में प्राप्त करने का अमूर्त Rectangleएक बुरा है।

यहां छवि विवरण दर्ज करें

Y'all को अन्य अमूल्य SOLID सिद्धांत प्रेरक पोस्टर की जाँच करनी चाहिए ।


19
@ m- शार्प क्या है अगर यह एक अपरिवर्तनीय आयत है जैसे कि SetWidth और SetHeight के बजाय, हमारे पास तरीके GetWidth और GetHeight हैं?
पचेरियर

139
कहानी का नैतिक: गुणों के आधार पर व्यवहार के आधार पर अपनी कक्षाओं को मॉडल करें; अपने डेटा को गुणों के आधार पर मॉडल करें और व्यवहार पर नहीं। यदि यह एक बतख की तरह व्यवहार करता है, तो यह निश्चित रूप से एक पक्षी है।
स्कालिव्ज़

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

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

14
सिद्धांत के बारे में मेरा एक सवाल है। अगर Square.setWidth(int width)इस तरह से लागू किया गया तो समस्या क्यों होगी this.width = width; this.height = width;:? इस मामले में यह गारंटी है कि चौड़ाई ऊंचाई के बराबर है।
एमसी सम्राट

488

लिस्कोव प्रतिस्थापन सिद्धांत (एलएसपी, ) ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग में एक अवधारणा है जो बताता है:

आधार वर्गों के लिए संकेत या संदर्भ का उपयोग करने वाले कार्य यह जानने के बिना व्युत्पन्न वर्गों की वस्तुओं का उपयोग करने में सक्षम होना चाहिए।

अपने दिल में एलएसपी इंटरफेस और कॉन्ट्रैक्ट्स के साथ-साथ यह भी तय करता है कि किसी वर्ग का विस्तार कब करना है या किसी अन्य रणनीति का उपयोग करें जैसे कि अपने लक्ष्य को प्राप्त करने के लिए रचना।

इस बिंदु को स्पष्ट करने के लिए मैंने सबसे प्रभावी तरीका हेड फ़र्स्ट ओओएएंडडी में देखा था । वे एक परिदृश्य प्रस्तुत करते हैं जहां आप रणनीति गेम के लिए एक रूपरेखा बनाने के लिए एक परियोजना पर एक डेवलपर हैं।

वे एक वर्ग प्रस्तुत करते हैं जो एक बोर्ड का प्रतिनिधित्व करता है जो इस तरह दिखता है:

कक्षा आरेख

सभी विधियां X और Y को दो-आयामी सरणी में टाइल की स्थिति का पता लगाने के लिए मापदंडों के रूप में समन्वयित करती हैं Tiles। यह गेम डेवलपर को गेम के दौरान बोर्ड में इकाइयों का प्रबंधन करने की अनुमति देगा।

पुस्तक यह कहने के लिए आवश्यकताओं को बदलने के लिए जाती है कि गेम फ्रेम काम को उन खेलों को समायोजित करने के लिए 3 डी गेम बोर्डों का भी समर्थन करना चाहिए जिनके पास उड़ान है। तो एक ThreeDBoardवर्ग पेश किया जाता है जो विस्तारित होता है Board

पहली नज़र में यह एक अच्छा निर्णय लगता है। Boardदोनों Heightऔर Widthगुण ThreeDBoardप्रदान करता है और Z अक्ष प्रदान करता है।

यह तब टूटता है जब आप विरासत में मिले अन्य सभी सदस्यों को देखते हैं Board। के लिए तरीके AddUnit, GetTile, GetUnitsऔर इतने पर, सब दोनों एक्स और वाई मापदंडों में ले Boardवर्ग लेकिन ThreeDBoardसाथ ही एक जेड पैरामीटर की जरूरत है।

तो आपको एक Z पैरामीटर के साथ उन तरीकों को फिर से लागू करना होगा। Z पैरामीटर का Boardवर्ग के लिए कोई संदर्भ नहीं है और कक्षा से विरासत में मिली विधियां Boardअपना अर्थ खो देती हैं। कोड का एक इकाई ThreeDBoardवर्ग को अपने आधार वर्ग के रूप में उपयोग करने का प्रयास कर रहा है Board

शायद हमें एक और दृष्टिकोण खोजना चाहिए। विस्तार करने के बजाय Board, वस्तुओं से ThreeDBoardबना होना चाहिए BoardBoardजेड अक्ष की प्रति इकाई एक वस्तु।

यह हमें एनकैप्सुलेशन और पुनः उपयोग जैसे अच्छे ऑब्जेक्ट उन्मुख सिद्धांतों का उपयोग करने की अनुमति देता है और एलएसपी का उल्लंघन नहीं करता है।


10
विकिपीडिया पर एक समान लेकिन सरल उदाहरण के लिए सर्कल-एलीप समस्या भी देखें ।
ब्रायन

@NotMySelf से अनुरोध करें: "मुझे लगता है कि उदाहरण केवल यह प्रदर्शित करने के लिए है कि बोर्ड से विरासत में तीनबर्डोर्ड के संदर्भ में कोई मतलब नहीं है और सभी विधि हस्ताक्षर एक Z अक्ष के साथ निरर्थक हैं।"
कंटैंगो

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

5
यह एक Liskov विरोधी उदाहरण है। Liskov हमें स्क्वायर से आयत निकालने के लिए बनाता है। कम मापदंडों-वर्ग से अधिक पैरामीटर-वर्ग। और आपने अच्छी तरह से दिखाया है कि यह बुरा है। उत्तर के रूप में चिह्नित किया जाना और लिस्कोव प्रश्न के लिए एंटी-लिस्कॉव उत्तर का 200 बार उत्तर देना वास्तव में एक अच्छा मजाक है। क्या लिस्कोव सिद्धांत वास्तव में एक अशुद्धता है?
गंगनुस

3
मैंने देखा है कि वंशानुक्रम गलत तरीके से काम करता है। यहाँ एक उदाहरण है। बेस क्लास 3DBoard और व्युत्पन्न क्लास बोर्ड होना चाहिए। बोर्ड फिर भी अधिकतम (जेड) = मिन (जेड) के एक Z अक्ष है = 1
Paulustrious

169

सबस्टिबिलिटी, ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग में एक सिद्धांत है, जिसमें कहा गया है कि, कंप्यूटर प्रोग्राम में, यदि S, T का उपप्रकार है, तो टाइप T की वस्तुओं को S के ऑब्जेक्ट से बदला जा सकता है।

आइए जावा में एक सरल उदाहरण देते हैं:

खराब उदाहरण

public class Bird{
    public void fly(){}
}
public class Duck extends Bird{}

बतख उड़ सकता है क्योंकि यह एक पक्षी है, लेकिन इसके बारे में क्या:

public class Ostrich extends Bird{}

शुतुरमुर्ग एक पक्षी है, लेकिन यह उड़ नहीं सकता है, शुतुरमुर्ग वर्ग पक्षी का एक उपप्रकार है, लेकिन यह मक्खी विधि का उपयोग नहीं कर सकता है, इसका मतलब है कि हम एलएसपी सिद्धांत को तोड़ रहे हैं।

अच्छा उदाहरण

public class Bird{
}
public class FlyingBirds extends Bird{
    public void fly(){}
}
public class Duck extends FlyingBirds{}
public class Ostrich extends Bird{} 

3
अच्छा उदाहरण है, लेकिन ग्राहक क्या होगा तो आप क्या करेंगे Bird bird। फ्लाइंग का उपयोग करने के लिए आपको फ्लाइंगबर्ड्स का ऑब्जेक्ट डालना होगा, जो अच्छा नहीं है?
मूडी

16
यदि ग्राहक के पास है Bird bird, तो इसका मतलब है कि वह उपयोग नहीं कर सकता है fly()। बस। पास होने से Duckयह तथ्य नहीं बदलता है। यदि क्लाइंट के पास है FlyingBirds bird, तो भले ही वह पास हो जाए लेकिन उसे Duckहमेशा उसी तरह से काम करना चाहिए।
स्टीव चामिलार्ड

9
यह भी इंटरफ़ेस अलगाव के लिए एक अच्छा उदाहरण के रूप में काम नहीं करेगा?
सहर्ष

उत्कृष्ट उदाहरण
थैंक्स

6
कैसे इंटरफ़ेस 'फ़्लाएबल' का उपयोग करने के बारे में (बेहतर नाम के बारे में सोच भी नहीं सकते)। इस तरह हम इस कठोर पदानुक्रम में खुद को प्रतिबद्ध नहीं करते हैं .. जब तक कि हम नहीं जानते कि वास्तव में इसकी आवश्यकता है।
थिरडी

132

एलएसपी चिंताओं से चिंतित है।

क्लासिक उदाहरण निम्नलिखित छद्म कोड घोषणा (छोड़ा गया कार्यान्वयन) द्वारा दिया गया है:

class Rectangle {
    int getHeight()
    void setHeight(int value)
    int getWidth()
    void setWidth(int value)
}

class Square : Rectangle { }

अब हमें एक समस्या है, हालांकि इंटरफ़ेस मेल खाता है। इसका कारण यह है कि हमने वर्गों और आयतों की गणितीय परिभाषा से उपजी आक्रमणकारियों का उल्लंघन किया है। जिस तरह से काम करता है और बसता है Rectangle, उसे निम्न आवेग को पूरा करना चाहिए:

void invariant(Rectangle r) {
    r.setHeight(200)
    r.setWidth(100)
    assert(r.getHeight() == 200 and r.getWidth() == 100)
}

हालाँकि, इस अपरिवर्तनीय का सही कार्यान्वयन द्वारा उल्लंघन किया जाना चाहिए Square, इसलिए यह एक वैध विकल्प नहीं है Rectangle


35
और इसलिए "ओओ" का उपयोग करने में कठिनाई कुछ भी है जिसे हम वास्तव में मॉडल करना चाहते हैं।
DrPizza

9
@DrPizza: बिल्कुल। हालाँकि, दो बातें। सबसे पहले, ऐसे संबंधों को अभी भी OOP में मॉडल किया जा सकता है, भले ही अपूर्ण रूप से या अधिक जटिल detours का उपयोग कर (जो भी आपकी समस्या को सूट करता है)। दूसरे, कोई बेहतर विकल्प नहीं है। अन्य मैपिंग / मोडलिंग में समान या समान समस्याएं हैं। ;-)
कोनराड रुडोल्फ

7
@NickW कुछ मामलों में (लेकिन ऊपर में नहीं) आप केवल वंशानुक्रम श्रृंखला को उल्टा कर सकते हैं - तार्किक रूप से, एक 2 डी बिंदु एक-एक 3 डी बिंदु है, जहां तीसरे आयाम की अवहेलना की जाती है (या 0 - सभी बिंदु एक ही विमान पर झूठ बोलते हैं 3 डी अंतरिक्ष)। लेकिन यह वास्तव में व्यावहारिक नहीं है। सामान्य तौर पर, यह उन मामलों में से एक है जहां विरासत वास्तव में मदद नहीं करती है, और संस्थाओं के बीच कोई प्राकृतिक संबंध मौजूद नहीं है। उन्हें अलग से मॉडल करें (कम से कम मुझे बेहतर तरीके से पता नहीं है)।
कोनराड रूडोल्फ

7
OOP का मतलब मॉडल व्यवहार और डेटा नहीं है। एलएसपी का उल्लंघन करने से पहले ही आपकी कक्षाएं एनकैप्सुलेशन का उल्लंघन करती हैं।
स्कालिव्ज़

2
@AustinWBryan हां; अब मैं इस क्षेत्र में काम कर रहा हूं, जितना अधिक मैं केवल इंटरफेस और एब्सट्रैक्ट बेस क्लास के लिए विरासत का उपयोग करता हूं, और बाकी के लिए रचना करता हूं। यह कभी-कभी थोड़ा और अधिक काम करता है (बुद्धिमान टाइपिंग) लेकिन यह समस्याओं का एक पूरा गुच्छा बचता है, और अन्य अनुभवी प्रोग्रामर द्वारा व्यापक रूप से प्रतिध्वनि सलाह है।
कोनराड रुडोल्फ

77

रॉबर्ट मार्टिन के पास Liskov प्रतिस्थापन सिद्धांत पर एक उत्कृष्ट पेपर है । यह सूक्ष्म और नहीं-तो-सूक्ष्म तरीकों पर चर्चा करता है जिसमें सिद्धांत का उल्लंघन हो सकता है।

कागज के कुछ प्रासंगिक हिस्से (ध्यान दें कि दूसरा उदाहरण बहुत घनीभूत है):

एलएसपी के उल्लंघन का एक सरल उदाहरण

इस सिद्धांत का सबसे भयावह उल्लंघन एक वस्तु के प्रकार के आधार पर एक फ़ंक्शन का चयन करने के लिए C ++ रन-टाइम टाइप सूचना (RTTI) का उपयोग है। अर्थात:

void DrawShape(const Shape& s)
{
  if (typeid(s) == typeid(Square))
    DrawSquare(static_cast<Square&>(s)); 
  else if (typeid(s) == typeid(Circle))
    DrawCircle(static_cast<Circle&>(s));
}

स्पष्ट रूप से DrawShapeफ़ंक्शन बुरी तरह से बनता है। इसे Shapeकक्षा के हर संभव व्युत्पन्न के बारे में पता होना चाहिए , और जब भी नया व्युत्पन्न बनता है, तो इसे बदलना होगा Shape। वास्तव में, कई इस फ़ंक्शन की संरचना को ऑब्जेक्ट ओरिएंटेड डिज़ाइन के लिए एंथम के रूप में देखते हैं।

स्क्वायर और आयत, एक अधिक सूक्ष्म उल्लंघन।

हालांकि, एलएसपी के उल्लंघन के तरीके अन्य, कहीं अधिक सूक्ष्म हैं। एक आवेदन पर विचार करें जो Rectangleनीचे वर्णित वर्ग का उपयोग करता है :

class Rectangle
{
  public:
    void SetWidth(double w) {itsWidth=w;}
    void SetHeight(double h) {itsHeight=w;}
    double GetHeight() const {return itsHeight;}
    double GetWidth() const {return itsWidth;}
  private:
    double itsWidth;
    double itsHeight;
};

[...] कल्पना कीजिए कि एक दिन उपयोगकर्ता आयतों के अलावा वर्गों में हेरफेर करने की क्षमता की मांग करते हैं। [...]

स्पष्ट रूप से, एक वर्ग सभी सामान्य इरादों और उद्देश्यों के लिए एक आयत है। चूंकि ISA संबंध रखता है, इसलिए Square वर्ग को व्युत्पन्न किया जाना तर्कसंगत है Rectangle। [...]

Squareकार्यों SetWidthऔर SetHeightकार्यों को विरासत में मिलेगा । ये कार्य एक के लिए पूरी तरह से अनुचित हैं Square, क्योंकि एक वर्ग की चौड़ाई और ऊंचाई समान हैं। यह एक महत्वपूर्ण सुराग होना चाहिए कि डिजाइन के साथ कोई समस्या है। हालांकि, समस्या को दूर करने का एक तरीका है। हम ओवरराइड कर सकते थे SetWidthऔर SetHeight[...]

लेकिन निम्नलिखित कार्य पर विचार करें:

void f(Rectangle& r)
{
  r.SetWidth(32); // calls Rectangle::SetWidth
}

यदि हम Squareइस फ़ंक्शन में किसी ऑब्जेक्ट का संदर्भ देते हैं , तो Squareऑब्जेक्ट दूषित हो जाएगा क्योंकि ऊँचाई को परिवर्तित नहीं किया जाएगा। यह एलएसपी का स्पष्ट उल्लंघन है। फ़ंक्शन अपने तर्कों के व्युत्पन्न के लिए काम नहीं करता है।

[...]


14
रास्ता देर से, लेकिन मुझे लगा कि यह उस पेपर में एक दिलचस्प उद्धरण था: Now the rule for the preconditions and postconditions for derivatives, as stated by Meyer is: ...when redefining a routine [in a derivative], you may only replace its precondition by a weaker one, and its postcondition by a stronger one. यदि एक बच्चे की कक्षा की पूर्व-स्थिति माता-पिता की पूर्व-स्थिति से अधिक मजबूत है, तो आप पूर्व-स्थिति का उल्लंघन किए बिना एक बच्चे को माता-पिता के लिए स्थानापन्न नहीं कर सकते। इसलिए एल.एस.पी.
user2023861

@ user2023861 आप बिल्कुल सही हैं। मैं इसके आधार पर उत्तर लिखूंगा।
inf3rno

40

एलएसपी आवश्यक है जहां कुछ कोड सोचते हैं कि यह एक प्रकार के तरीकों को बुला रहा है T, और अनजाने में एक प्रकार के तरीकों को कॉल कर सकता है S, जहां S extends T(अर्थात S, इनहेरिट्स से व्युत्पन्न होता है, या इसका एक उपप्रकार, सुपरटाइप है T)।

उदाहरण के लिए, यह होता है जहां एक प्रकार के इनपुट पैरामीटर के साथ एक फ़ंक्शन T, प्रकार के तर्क मान के साथ (यानी आह्वान किया जाता है) S। या, जहां प्रकार का एक पहचानकर्ता, प्रकार का Tमान असाइन किया गया है S

val id : T = new S() // id thinks it's a T, but is a S

एलएसपी को प्रकार के तरीकों T(जैसे ) के लिए अपेक्षाओं (यानी अपरिवर्तनीयों) की आवश्यकता होती है Rectangle, जब प्रकार के तरीकों S(जैसे Square) के बजाय इसका उल्लंघन नहीं किया जाता है।

val rect : Rectangle = new Square(5) // thinks it's a Rectangle, but is a Square
val rect2 : Rectangle = rect.setWidth(10) // height is 10, LSP violation

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

class Rectangle( val width : Int, val height : Int )
{
   def setWidth( w : Int ) = new Rectangle(w, height)
   def setHeight( h : Int ) = new Rectangle(width, h)
}

class Square( val side : Int ) extends Rectangle(side, side)
{
   override def setWidth( s : Int ) = new Square(s)
   override def setHeight( s : Int ) = new Square(s)
}

एलएसपी के लिए आवश्यक है कि उपप्रकार के प्रत्येक तरीके में Sकंट्रावेरेंट इनपुट पैरामीटर (एस) और एक सहसंयोजक आउटपुट होना चाहिए।

कंट्रावेरिएंट का अर्थ है कि विचरण विरासत की दिशा के विपरीत है, अर्थात Si, उपप्रकार की प्रत्येक विधि के प्रत्येक इनपुट पैरामीटर का प्रकार S, समान या सुपरपेप्ट के Tiसंबंधित विधि के संबंधित इनपुट पैरामीटर के प्रकार का एक सुपरस्क्रिप्ट होना चाहिए। T

कोवरिएनस का अर्थ है कि विचरण विरासत की एक ही दिशा में है, अर्थात So, उपप्रकार के प्रत्येक विधि के आउटपुट का प्रकार S, समान या उपप्रकार के Toसंबंधित विधि के संबंधित आउटपुट के प्रकार का एक उपप्रकार होना चाहिए T

ऐसा इसलिए है क्योंकि यदि कॉलर सोचता है कि उसके पास एक प्रकार है T, तो सोचता है कि यह एक विधि कह रहा है T, तो यह प्रकार के तर्क (ओं) की आपूर्ति Tiकरता है और प्रकार को आउटपुट प्रदान करता है To। जब यह वास्तव में की इसी विधि को बुला रहा है S, तो प्रत्येक Tiइनपुट तर्क को एक Siइनपुट पैरामीटर सौंपा जाता है , और Soआउटपुट प्रकार को सौंपा जाता है To। इस प्रकार, यदि Sicontravariant को wrt नहीं किया गया था Ti, तो एक उपप्रकार- Xiजो कि उप-प्रकार नहीं होगा, Siको सौंपा जाएगा Ti

इसके अतिरिक्त, भाषाओं के लिए (जैसे स्काला या सीलोन), जिसमें टाइप पॉलीमॉर्फिज़्म पैरामीटर (यानी जेनरिक) पर परिभाषा-साइट विचरण एनोटेशन होते हैं, प्रत्येक प्रकार के पैरामीटर के लिए विचरण एनोटेशन के सह- या इंजेक्शन- दिशा विपरीत या समान दिशा Tहोनी चाहिए क्रमशः हर इनपुट पैरामीटर या आउटपुट (हर विधि का ) जिसमें टाइप पैरामीटर होता है।T

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


सबटाइपिंग उपयुक्त है जहां आक्रमणकारियों को एन्यूमरेट किया जा सकता है।

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

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

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

मेरी सैद्धांतिक स्थिति यह है कि ज्ञान के अस्तित्व के लिए (देखें अनुभाग "केंद्रीयकरण अंधा और अयोग्य है"), एक सामान्य मॉडल कभी नहीं होगा जो ट्यूरिंग-पूर्ण कंप्यूटर भाषा में सभी संभावित आक्रमणकारियों के 100% कवरेज को लागू कर सकता है। ज्ञान के अस्तित्व के लिए, अप्रत्याशित संभावनाएं बहुत मौजूद हैं, अर्थात विकार और एन्ट्रापी हमेशा बढ़ते रहना चाहिए। यह एन्ट्रापिक बल है। एक संभावित विस्तार के सभी संभावित संगणनाओं को साबित करने के लिए, प्राथमिकताओं को सभी संभव विस्तार की गणना करना है।

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

इन प्रमेयों की एक व्याख्या उन्हें एन्ट्रापिक बल की सामान्यीकृत वैचारिक समझ में शामिल करती है:

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

17
@ शाल्बी: आपने बहुत सी चीजों को मिलाया है। चीजें उतनी भ्रमित नहीं हैं जितनी आप उन्हें बताते हैं। आपके बहुत से सैद्धांतिक दावे भड़कीले आधारों पर खड़े हैं, जैसे 'ज्ञान के लिए, अप्रत्याशित संभावनाएं मौजूद हैं, .........' और 'आमतौर पर यह एक असंदिग्ध समस्या है कि क्या कोई सेट दूसरे का सबसेट है, यानी वंशानुक्रम आम तौर पर अनिर्दिष्ट है '। आप इनमें से प्रत्येक बिंदु के लिए एक अलग ब्लॉग शुरू कर सकते हैं। वैसे भी, आपके दावे और धारणाएं अत्यधिक संदिग्ध हैं। किसी को उन चीजों का उपयोग नहीं करना चाहिए जिनके बारे में पता नहीं है!
एकनोन डे 27'13

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

4
@ShelbyMooreIII आप बहुत अधिक दिशाओं में जा रहे हैं। यह कोई उत्तर नहीं है।
सोल्लम

1
@Soldalma यह एक जवाब है। क्या आप इसे उत्तर अनुभाग में नहीं देखते हैं। आपकी एक टिप्पणी है क्योंकि यह टिप्पणी अनुभाग में है।
शेल्बी मूर तृतीय 12

1
अपने scala दुनिया के साथ मिश्रण की तरह!
एहसान एम। करमानी

23

यह निर्धारित करने के लिए एक जांच सूची है कि आप लिस्कोव का उल्लंघन कर रहे हैं या नहीं।

  • यदि आप निम्न में से किसी एक वस्तु का उल्लंघन करते हैं -> आप लिस्कोव का उल्लंघन करते हैं।
  • यदि आप किसी भी उल्लंघन नहीं करते हैं -> कुछ भी नहीं कर सकते।

सूची देखें:

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

    public class SuperType
    {
        public string Name { get; private set; }
        public SuperType(string name, int age)
        {
            Name = name;
            Age = age;
        }
    }
    public class SubType : SuperType
    {
        public void ChangeName(string newName)
        {
            var propertyType = base.GetType().GetProperty("Name").SetValue(this, newName);
        }
    }
    

2 अन्य आइटम हैं: विधि तर्कों का कंट्रोवर्सी और रिटर्न प्रकार के कोवरियन । लेकिन यह C # में संभव नहीं है (मैं C # डेवलपर हूं) इसलिए मुझे उनकी परवाह नहीं है।

संदर्भ:


मैं C # डेवलपर भी हूं और मैं बताऊंगा कि आपका अंतिम कथन .net 4.0 फ्रेमवर्क के साथ विज़ुअल स्टूडियो 2010 के अनुसार सही नहीं है। वापसी प्रकार का सहसंयोजक इंटरफ़ेस द्वारा परिभाषित की तुलना में अधिक व्युत्पन्न वापसी प्रकार की अनुमति देता है। उदाहरण: उदाहरण: IEnumerable <T> (T covariant है) IEnumerator <T> (T covariant है) IQueryable <T> (T covariant है) IGrouping <TKey, Telement> (TKey and Telement are covariant) IComparer <T> (T) is contravariant) IEqualityComparer <T> (T contravariant है) IComparable <T> (T contravariant) msdn.microsoft.com/en-us/library/dd233059/v=vs.100).aspx
LCarter

1
महान और केंद्रित उत्तर (हालांकि मूल प्रश्न नियमों से अधिक उदाहरणों के बारे में थे)।
माइक

23

मैं हर जवाब में आयतों और चौकों को देखता हूं, और एलएसपी का उल्लंघन कैसे करता हूं।

मैं यह दिखाना चाहता हूं कि एक वास्तविक दुनिया के उदाहरण के साथ एलएसपी कैसे अनुरूप हो सकता है:

<?php

interface Database 
{
    public function selectQuery(string $sql): array;
}

class SQLiteDatabase implements Database
{
    public function selectQuery(string $sql): array
    {
        // sqlite specific code

        return $result;
    }
}

class MySQLDatabase implements Database
{
    public function selectQuery(string $sql): array
    {
        // mysql specific code

        return $result; 
    }
}

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

और हाँ, आप इस विन्यास में एलएसपी का उल्लंघन कर सकते हैं जैसे कि एक साधारण परिवर्तन करना:

<?php

interface Database 
{
    public function selectQuery(string $sql): array;
}

class SQLiteDatabase implements Database
{
    public function selectQuery(string $sql): array
    {
        // sqlite specific code

        return $result;
    }
}

class MySQLDatabase implements Database
{
    public function selectQuery(string $sql): array
    {
        // mysql specific code

        return ['result' => $result]; // This violates LSP !
    }
}

अब उप-प्रकारों का उपयोग उसी तरह से नहीं किया जा सकता है क्योंकि वे अब और समान परिणाम नहीं देते हैं।


6
उदाहरण केवल तब तक हम के शब्दों विवश रूप LSP का उल्लंघन नहीं करता है Database::selectQueryसमर्थन सिर्फ एसक्यूएल के सबसेट द्वारा समर्थित करने के लिए सभी डीबी इंजन। यह शायद ही व्यावहारिक है ... यह कहा, उदाहरण अभी भी यहाँ इस्तेमाल किया अन्य लोगों की तुलना में समझ आसान है।
पालक

5
मैंने इस उत्तर को बाकी के मुकाबले सबसे आसान समझा।
माल्कम साल्वाडोर

22

एलएसपी क्लैस के अनुबंध के बारे में एक नियम है: यदि एक बेस क्लास एक अनुबंध को संतुष्ट करता है, तो एलएसपी द्वारा व्युत्पन्न वर्गों को भी उस अनुबंध को पूरा करना होगा।

स्यूडो-पायथन में

class Base:
   def Foo(self, arg): 
       # *... do stuff*

class Derived(Base):
   def Foo(self, arg):
       # *... do stuff*

एलएसपी को संतुष्ट करता है यदि हर बार जब आप फू को एक व्युत्पन्न वस्तु पर कहते हैं, तो यह ठीक उसी तरह से परिणाम देता है जैसे फू को बेस ऑब्जेक्ट पर कॉल करना, जब तक कि आरजी एक ही है।


9
लेकिन ... यदि आप हमेशा एक ही व्यवहार करते हैं, तो व्युत्पन्न वर्ग होने का क्या मतलब है?
लियोनिद

2
आपने एक बिंदु याद किया: यह एक ही मनाया गया व्यवहार है। उदाहरण के लिए, आप कुछ को O (n) प्रदर्शन के साथ कार्यात्मक रूप से समकक्ष के साथ बदल सकते हैं, लेकिन O (lg n) प्रदर्शन के साथ। या आप कुछ ऐसा कर सकते हैं जो MySQL के साथ कार्यान्वित डेटा तक पहुँचता है और इसे एक इन-मेमोरी डेटाबेस के साथ बदल देता है।
चार्ली मार्टिन

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

खैर, आपको वापस जाने और बारबरा के मूल पेपर की जांच करने की आवश्यकता है। रिपोर्ट्स-archive.adm.cs.cmu.edu/anon/1999/CMU-CS-99-156.ps यह वास्तव में इंटरफेस के संदर्भ में नहीं कहा गया है, और यह किसी भी तरह से (या नहीं) एक तार्किक संबंध है। प्रोग्रामिंग भाषा जिसमें विरासत का कुछ रूप है।
चार्ली मार्टिन

1
@ HamishGrubijan मुझे नहीं पता कि आपको किसने बताया कि पायथन दृढ़ता से टाइप नहीं किया गया है, लेकिन वे आपसे झूठ बोल रहे थे (और अगर आप मुझ पर विश्वास नहीं करते हैं, तो पायथन दुभाषिया और कोशिश करें 2 + "2")। शायद आप "स्टेटिकली टाइप्ड" के साथ "दृढ़ता से टाइप" भ्रमित करते हैं?
asmeurer

21

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

चलो कहते हैं कि तुम एक है आधार ItemsRepository।

class ItemsRepository
{
    /**
    * @return int Returns number of deleted rows
    */
    public function delete()
    {
        // perform a delete query
        $numberOfDeletedRows = 10;

        return $numberOfDeletedRows;
    }
}

और एक उप वर्ग इसका विस्तार कर रहा है:

class BadlyExtendedItemsRepository extends ItemsRepository
{
    /**
     * @return void Was suppose to return an INT like parent, but did not, breaks LSP
     */
    public function delete()
    {
        // perform a delete query
        $numberOfDeletedRows = 10;

        // we broke the behaviour of the parent class
        return;
    }
}

तब आपके पास आधार आइटम्स के साथ काम करने वाला एक क्लाइंट हो सकता है ।

/**
 * Class ItemsService is a client for public ItemsRepository "API" (the public delete method).
 *
 * Technically, I am able to pass into a constructor a sub-class of the ItemsRepository
 * but if the sub-class won't abide the base class API, the client will get broken.
 */
class ItemsService
{
    /**
     * @var ItemsRepository
     */
    private $itemsRepository;

    /**
     * @param ItemsRepository $itemsRepository
     */
    public function __construct(ItemsRepository $itemsRepository)
    {
        $this->itemsRepository = $itemsRepository;
    }

    /**
     * !!! Notice how this is suppose to return an int. My clients expect it based on the
     * ItemsRepository API in the constructor !!!
     *
     * @return int
     */
    public function delete()
    {
        return $this->itemsRepository->delete();
    }
} 

LSP जब टूट गया है प्रतिस्थापन माता-पिता एक साथ वर्ग उप वर्ग टूटता एपीआई अनुबंध

class ItemsController
{
    /**
     * Valid delete action when using the base class.
     */
    public function validDeleteAction()
    {
        $itemsService = new ItemsService(new ItemsRepository());
        $numberOfDeletedItems = $itemsService->delete();

        // $numberOfDeletedItems is an INT :)
    }

    /**
     * Invalid delete action when using a subclass.
     */
    public function brokenDeleteAction()
    {
        $itemsService = new ItemsService(new BadlyExtendedItemsRepository());
        $numberOfDeletedItems = $itemsService->delete();

        // $numberOfDeletedItems is a NULL :(
    }
}

आप मेरे पाठ्यक्रम में बनाए रखने योग्य सॉफ़्टवेयर लिखने के बारे में अधिक जान सकते हैं: https://www.udemy.com/enterprise-php/


20

आधार वर्गों के लिए संकेत या संदर्भ का उपयोग करने वाले कार्य यह जानने के बिना व्युत्पन्न वर्गों की वस्तुओं का उपयोग करने में सक्षम होना चाहिए।

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

अवधारणा पर अधिक पढ़ने के बाद हालांकि मैंने पाया कि एलएसपी को आम तौर पर उससे अधिक व्यापक रूप से व्याख्या किया जाता है।

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

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

हाथ में इस ज्ञान के साथ, एलएसपी पालन का मूल्यांकन यह निर्धारित करने में एक महान उपकरण हो सकता है कि जब संरचना विरासत के बजाय मौजूदा कार्यक्षमता को बढ़ाने के लिए अधिक उपयुक्त तंत्र है।


19

मुझे लगता है कि एलएसपी तकनीकी रूप से कवर किए गए सभी प्रकार के हैं: आप मूल रूप से उप-प्रकार के विवरण से दूर रहना चाहते हैं और सुपरपाइप का सुरक्षित रूप से उपयोग करना चाहते हैं।

तो लिस्कोव के 3 अंतर्निहित नियम हैं:

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

  2. विधियाँ नियम: उन कार्यों का कार्यान्वयन शब्दार्थ ध्वनि है।

    • कमजोर पूर्वधारणाएँ: उपप्रकार कार्यों को कम से कम लेना चाहिए कि सुपरपेप्ट ने इनपुट के रूप में क्या लिया, यदि अधिक नहीं।
    • मजबूत पोस्टकंडिशन: उन्हें उत्पादित सुपरटेप विधियों के उत्पादन का सबसेट का उत्पादन करना चाहिए।
  3. गुण नियम: यह व्यक्तिगत फ़ंक्शन कॉल से परे जाता है।

    • अपरिवर्तनीय: चीजें जो हमेशा सच होती हैं, उन्हें सच रहना चाहिए। उदाहरण के लिए। एक सेट का आकार कभी भी नकारात्मक नहीं होता है।
    • विकासवादी गुण: आमतौर पर किसी चीज को अपरिवर्तनीयता या उस तरह के राज्यों के साथ जो वस्तु में हो सकती है। या हो सकता है कि वस्तु केवल बढ़ती है और कभी सिकुड़ती नहीं है, इसलिए उपप्रकार के तरीके इसे नहीं बनाना चाहिए।

इन सभी गुणों को संरक्षित करने की आवश्यकता है और अतिरिक्त उपप्रकार कार्यक्षमता को सुपरटाइप गुणों का उल्लंघन नहीं करना चाहिए।

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

स्रोत: जावा में प्रोग्राम डेवलपमेंट - बारबरा लिस्कॉव


18

सॉफ्टवेयर परीक्षण में एलएसपी के उपयोग का एक महत्वपूर्ण उदाहरण है ।

अगर मेरे पास एक वर्ग ए है जो बी का एक एलएसपी-अनुपालन उपवर्ग है, तो मैं ए का परीक्षण करने के लिए बी के टेस्ट सूट का पुन: उपयोग कर सकता हूं।

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

यह महसूस करने का एक तरीका यह है कि मैकग्रेगर ने "परीक्षण के लिए समानांतर पदानुक्रम" का निर्माण किया है: मेरा ATestवर्ग इससे विरासत में मिला होगा BTest। इंजेक्शन के कुछ रूप को तब टाइप बी की बजाय टेस्ट ए की वस्तुओं के साथ काम करने के लिए सुनिश्चित करने की आवश्यकता होती है (एक सरल टेम्पलेट विधि पैटर्न करेगा)।

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

Stackoverflow सवाल का जवाब भी देखें " क्या मैं इंटरफ़ेस के कार्यान्वयन का परीक्षण करने के लिए पुन: प्रयोज्य परीक्षणों की एक श्रृंखला को लागू कर सकता हूं? "


13

आइए जावा में उदाहरण देते हैं:

class TrasportationDevice
{
   String name;
   String getName() { ... }
   void setName(String n) { ... }

   double speed;
   double getSpeed() { ... }
   void setSpeed(double d) { ... }

   Engine engine;
   Engine getEngine() { ... }
   void setEngine(Engine e) { ... }

   void startEngine() { ... }
}

class Car extends TransportationDevice
{
   @Override
   void startEngine() { ... }
}

यहाँ कोई समस्या नहीं है, है ना? एक कार निश्चित रूप से एक परिवहन उपकरण है, और यहां हम देख सकते हैं कि यह अपने सुपरक्लास की शुरुआत () विधि को ओवरराइड करता है।

आइए एक और परिवहन उपकरण जोड़ें:

class Bicycle extends TransportationDevice
{
   @Override
   void startEngine() /*problem!*/
}

सब कुछ योजना के अनुसार नहीं हो रहा है! हां, एक साइकिल एक परिवहन उपकरण है, हालांकि, इसमें इंजन नहीं है और इसलिए, विधि startEngine () को लागू नहीं किया जा सकता है।

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

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

हम इस प्रकार के रूप में हमारे TransportDevice वर्ग refactor कर सकते हैं:

class TrasportationDevice
{
   String name;
   String getName() { ... }
   void setName(String n) { ... }

   double speed;
   double getSpeed() { ... }
   void setSpeed(double d) { ... }
}

अब हम गैर-मोटर चालित उपकरणों के लिए TransportDevice का विस्तार कर सकते हैं।

class DevicesWithoutEngines extends TransportationDevice
{  
   void startMoving() { ... }
}

और मोटर चालित उपकरणों के लिए TransportDevice का विस्तार करें। इंजन ऑब्जेक्ट को जोड़ने के लिए यहां अधिक उपयुक्त है।

class DevicesWithEngines extends TransportationDevice
{  
   Engine engine;
   Engine getEngine() { ... }
   void setEngine(Engine e) { ... }

   void startEngine() { ... }
}

इस प्रकार हमारी कार क्लास अधिक विशिष्ट हो जाती है, जबकि लिस्कोव सबस्टीट्यूशन सिद्धांत का पालन करना।

class Car extends DevicesWithEngines
{
   @Override
   void startEngine() { ... }
}

और हमारा साइकिल वर्ग भी लिस्कोव सबस्टीट्यूशन सिद्धांत के अनुपालन में है।

class Bicycle extends DevicesWithoutEngines
{
   @Override
   void startMoving() { ... }
}

9

एलएसपी का यह सूत्रीकरण बहुत मजबूत है:

यदि टाइप S के प्रत्येक ऑब्जेक्ट o1 के लिए, T का ऑब्जेक्ट o2 है, तो ऐसे सभी प्रोग्रामों के लिए, जो P के लिए T के संदर्भ में हैं, P का व्यवहार अपरिवर्तित है, जब O1 को O2 के लिए प्रतिस्थापित किया जाता है, तो S, T का एक उपप्रकार है।

जिसका मूल रूप से मतलब है कि एस एक और है, पूरी तरह से टी के रूप में एक ही चीज के कार्यान्वयन को पूरी तरह से समझाया गया है और मैं बोल्ड हो सकता है और यह तय कर सकता हूं कि प्रदर्शन पी के व्यवहार का हिस्सा है ...

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

विकिपीडिया द्वारा उद्धृत सूत्रीकरण बेहतर है क्योंकि संपत्ति संदर्भ पर निर्भर करती है और कार्यक्रम के पूरे व्यवहार को आवश्यक रूप से शामिल नहीं करती है।


2
एर्म, वह सूत्रधार बारबरा लिस्कॉव का अपना है। बारबरा लिस्कोव, "डेटा एब्स्ट्रेक्शन और पदानुक्रम," सिगनलन नोटिस, 23,5 (मई, 1988)। यह "रास्ता बहुत मजबूत" नहीं है, यह "बिल्कुल सही" है, और इसका मतलब यह नहीं है कि आपको लगता है कि यह है। यह मजबूत है, लेकिन ताकत की सही मात्रा है।
DrPizza 7

फिर, वास्तविक जीवन में बहुत कम उपप्रकार हैं :)
डेमियन पोललेट

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

9

एक बहुत ही सरल वाक्य में, हम कह सकते हैं:

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


9

लिस्कोव के प्रतिस्थापन सिद्धांत (एलएसपी)

हर समय हम एक प्रोग्राम मॉड्यूल डिजाइन करते हैं और हम कुछ वर्ग पदानुक्रम बनाते हैं। फिर हम कुछ वर्गों को कुछ व्युत्पन्न वर्गों का विस्तार करते हैं।

हमें यह सुनिश्चित करना चाहिए कि नई व्युत्पन्न कक्षाएं पुरानी कक्षाओं की कार्यक्षमता को बदलने के बिना ही विस्तारित हों। अन्यथा, नई कक्षाएं मौजूदा प्रोग्राम मॉड्यूल में उपयोग किए जाने पर अवांछित प्रभाव पैदा कर सकती हैं।

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

उदाहरण:

नीचे क्लासिक उदाहरण है जिसके लिए लिस्कोव के प्रतिस्थापन सिद्धांत का उल्लंघन किया गया है। उदाहरण में, 2 वर्गों का उपयोग किया जाता है: आयत और वर्ग। आइए मान लें कि आयत वस्तु का उपयोग कहीं अनुप्रयोग में किया गया है। हम एप्लिकेशन का विस्तार करते हैं और स्क्वायर क्लास को जोड़ते हैं। वर्ग वर्ग को कुछ शर्तों के आधार पर, फ़ैक्टरी पैटर्न द्वारा लौटाया जाता है और हमें यह नहीं पता होता है कि किस प्रकार की वस्तु वापस आएगी। लेकिन हम जानते हैं कि यह एक आयत है। हम आयत वस्तु प्राप्त करते हैं, चौड़ाई 5 और ऊंचाई 10 पर सेट करते हैं और क्षेत्र प्राप्त करते हैं। चौड़ाई 5 और ऊंचाई 10 के साथ एक आयत के लिए, क्षेत्र 50 होना चाहिए। इसके बजाय, परिणाम 100 होगा

    // Violation of Likov's Substitution Principle
class Rectangle {
    protected int m_width;
    protected int m_height;

    public void setWidth(int width) {
        m_width = width;
    }

    public void setHeight(int height) {
        m_height = height;
    }

    public int getWidth() {
        return m_width;
    }

    public int getHeight() {
        return m_height;
    }

    public int getArea() {
        return m_width * m_height;
    }
}

class Square extends Rectangle {
    public void setWidth(int width) {
        m_width = width;
        m_height = width;
    }

    public void setHeight(int height) {
        m_width = height;
        m_height = height;
    }

}

class LspTest {
    private static Rectangle getNewRectangle() {
        // it can be an object returned by some factory ...
        return new Square();
    }

    public static void main(String args[]) {
        Rectangle r = LspTest.getNewRectangle();

        r.setWidth(5);
        r.setHeight(10);
        // user knows that r it's a rectangle.
        // It assumes that he's able to set the width and height as for the base
        // class

        System.out.println(r.getArea());
        // now he's surprised to see that the area is 100 instead of 50.
    }
}

निष्कर्ष:

यह सिद्धांत केवल ओपन क्लोज प्रिंसिपल का एक विस्तार है और इसका मतलब है कि हमें यह सुनिश्चित करना होगा कि नए व्युत्पन्न वर्ग अपने व्यवहार को बदले बिना आधार वर्गों का विस्तार कर रहे हैं।

इसे भी देखें: ओपन प्रिंसिपल

बेहतर संरचना के लिए कुछ समान अवधारणाएं: कॉन्फ़िगरेशन पर कन्वेंशन


8

लिस्कोव प्रतिस्थापन सिद्धांत

  • ओवरराइड विधि खाली नहीं रहनी चाहिए
  • ओवरराइड की गई विधि में कोई त्रुटि नहीं होनी चाहिए
  • बेस क्लास या इंटरफ़ेस का व्यवहार व्युत्पन्न वर्ग व्यवहार के कारण संशोधन (rework) के लिए नहीं जाना चाहिए।

7

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

  • बेस क्लास के इन-वेरिएंट को व्युत्पन्न वर्ग द्वारा संरक्षित किया जाना चाहिए
  • बेस क्लास की पूर्व-शर्तों को व्युत्पन्न वर्ग द्वारा मजबूत नहीं किया जाना चाहिए
  • बेस क्लास की पोस्ट-शर्तों को व्युत्पन्न वर्ग द्वारा कमजोर नहीं किया जाना चाहिए।

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

मेरे ब्लॉग पर उपलब्ध इस पर आगे की चर्चा: लिस्कोव प्रतिस्थापन सिद्धांत


6

एलएसपी सरल शब्दों में कहता है कि एक ही सुपरक्लास की वस्तुओं को एक -दूसरे के साथ बिना किसी चीज को तोड़ने के लिए स्वैप किया जाना चाहिए ।

उदाहरण के लिए, यदि हमारे पास एक Catऔर एक Dogवर्ग से व्युत्पन्न Animalवर्ग है, तो पशु वर्ग का उपयोग करने वाले किसी भी कार्य को सामान्य रूप से उपयोग Catया Dogव्यवहार करने में सक्षम होना चाहिए ।


4

बोर्ड की एक सरणी के संदर्भ में थ्रीडीबर्ड को लागू करना क्या उपयोगी होगा?

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

बाहरी इंटरफ़ेस के संदर्भ में, आप TwoDBoard और ThreeDBoard दोनों के लिए एक बोर्ड इंटरफ़ेस को फ़ैक्टर करना चाह सकते हैं (हालाँकि उपरोक्त विधियों में से कोई भी उपयुक्त नहीं है)।


1
मुझे लगता है कि उदाहरण बस यह प्रदर्शित करने के लिए है कि बोर्ड से विरासत में थ्रीडीबर्ड के संदर्भ में कोई मतलब नहीं है और सभी विधि हस्ताक्षर एक जेड अक्ष के साथ अर्थहीन हैं।
NotMyself

4

एक वर्ग एक आयत है जहाँ चौड़ाई ऊँचाई के बराबर होती है। यदि वर्ग चौड़ाई और ऊँचाई के लिए दो अलग-अलग आकार निर्धारित करता है तो यह वर्ग अशुद्धि का उल्लंघन करता है। यह साइड इफेक्ट्स शुरू करके चारों ओर काम किया है। लेकिन अगर आयत में पूर्व निर्धारित 0 <ऊंचाई और 0 <चौड़ाई के साथ एक सेटसाइज़ (ऊंचाई, चौड़ाई) थी। व्युत्पन्न उपप्रकार विधि में ऊंचाई == चौड़ाई की आवश्यकता होती है; एक मजबूत पूर्व शर्त (और जो lsp का उल्लंघन करता है)। इससे पता चलता है कि हालांकि वर्ग एक आयत है, यह एक मान्य उपप्रकार नहीं है क्योंकि पूर्व शर्त मजबूत हो जाती है। आस-पास के कार्य (सामान्य रूप से एक बुरी चीज) एक साइड इफेक्ट का कारण बनते हैं और इससे पोस्ट की स्थिति कमजोर हो जाती है (जो एलएसपी का उल्लंघन करती है)। आधार पर setWidth में पोस्ट की स्थिति 0 <चौड़ाई है। व्युत्पन्न इसे ऊंचाई == चौड़ाई के साथ कमजोर करता है।

इसलिए एक आकार बदलने योग्य वर्ग एक आकार बदलने योग्य आयत नहीं है।


4

यह सिद्धांत 1987 में बारबरा लिस्कोव द्वारा पेश किया गया था और एक सुपरक्लास और इसके उपप्रकारों के व्यवहार पर ध्यान केंद्रित करके ओपन-क्लोज्ड सिद्धांत का विस्तार करता है।

इसका महत्व तब स्पष्ट हो जाता है जब हम इसका उल्लंघन करने के परिणामों पर विचार करते हैं। निम्नलिखित वर्ग का उपयोग करने वाले एप्लिकेशन पर विचार करें।

public class Rectangle 
{ 
  private double width;

  private double height; 

  public double Width 
  { 
    get 
    { 
      return width; 
    } 
    set 
    { 
      width = value; 
    }
  } 

  public double Height 
  { 
    get 
    { 
      return height; 
    } 
    set 
    { 
      height = value; 
    } 
  } 
}

कल्पना कीजिए कि एक दिन, ग्राहक आयतों के अलावा वर्गों में हेरफेर करने की क्षमता की मांग करता है। चूंकि एक वर्ग एक आयत है, वर्ग वर्ग को आयत वर्ग से लिया जाना चाहिए।

public class Square : Rectangle
{
} 

हालाँकि, ऐसा करने से हम दो समस्याओं का सामना करेंगे:

एक वर्ग को आयत से विरासत में मिली ऊँचाई और चौड़ाई दोनों प्रकार के चर की आवश्यकता नहीं होती है और यह स्मृति में महत्वपूर्ण बर्बादी पैदा कर सकता है यदि हमें सैकड़ों हजारों वर्ग ऑब्जेक्ट बनाने हैं। आयत से विरासत में मिली चौड़ाई और ऊंचाई सेटर गुण एक वर्ग के लिए अनुपयुक्त हैं क्योंकि एक वर्ग की चौड़ाई और ऊंचाई समान हैं। समान मान के लिए ऊंचाई और चौड़ाई दोनों सेट करने के लिए, हम दो नए गुण बना सकते हैं:

public class Square : Rectangle
{
  public double SetWidth 
  { 
    set 
    { 
      base.Width = value; 
      base.Height = value; 
    } 
  } 

  public double SetHeight 
  { 
    set 
    { 
      base.Height = value; 
      base.Width = value; 
    } 
  } 
}

अब, जब कोई एक वर्ग वस्तु की चौड़ाई निर्धारित करेगा, तो इसकी ऊंचाई तदनुसार और इसके विपरीत बदल जाएगी।

Square s = new Square(); 
s.SetWidth(1); // Sets width and height to 1. 
s.SetHeight(2); // sets width and height to 2. 

आइए आगे बढ़ते हैं और इस अन्य फ़ंक्शन पर विचार करते हैं:

public void A(Rectangle r) 
{ 
  r.SetWidth(32); // calls Rectangle.SetWidth 
} 

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

हालांकि, सेटर के गुणों को आभासी घोषित करने से हमें एक और उल्लंघन का सामना करना पड़ेगा, ओ.सी.पी. वास्तव में, एक व्युत्पन्न वर्ग वर्ग का निर्माण आधार वर्ग आयत में परिवर्तन का कारण बन रहा है।


3

एलएसपी के लिए मुझे अब तक का सबसे स्पष्ट स्पष्टीकरण "लिस्कोव सबस्टीट्यूशन सिद्धांत कहता है कि एक व्युत्पन्न वर्ग का ऑब्जेक्ट सिस्टम में किसी भी त्रुटि को लाए बिना बेस क्लास के ऑब्जेक्ट को बदलने या बेस क्लास के व्यवहार को संशोधित करने में सक्षम होना चाहिए। “ यहाँ से । लेख एलएसपी का उल्लंघन करने और इसे ठीक करने के लिए कोड उदाहरण देता है।


1
कृपया stackoverflow पर कोड के उदाहरण प्रदान करें।
1963 में

3

मान लीजिए कि हम अपने कोड में एक आयत का उपयोग करते हैं

r = new Rectangle();
// ...
r.setDimensions(1,2);
r.fill(colors.red());
canvas.draw(r);

हमारे ज्यामिति वर्ग में हमने जाना कि एक वर्ग एक विशेष प्रकार का आयत है क्योंकि इसकी चौड़ाई इसकी ऊँचाई जितनी है। आइए Squareइस जानकारी के आधार पर एक क्लास बनाएं :

class Square extends Rectangle {
    setDimensions(width, height){
        assert(width == height);
        super.setDimensions(width, height);
    }
} 

अगर हम बदलने के Rectangleसाथ Squareहमारा पहला कोड में है, तो यह टूट जाएगा:

r = new Square();
// ...
r.setDimensions(1,2); // assertion width == height failed
r.fill(colors.red());
canvas.draw(r);

इसका कारण यह है कि Squareएक नई पूर्व शर्त है कि हमारे पास Rectangleकक्षा में नहीं है width == height:। एलएसपी के अनुसार Rectangleउदाहरणों को Rectangleउपवर्ग उदाहरणों के साथ प्रतिस्थापित किया जाना चाहिए । ऐसा इसलिए है क्योंकि ये उदाहरण इंस्टेंसेस के लिए टाइप चेक पास करते हैं Rectangleऔर इसलिए वे आपके कोड में अप्रत्याशित त्रुटियां पैदा करेंगे।

यह विकी लेख में "पूर्व शर्त को एक उपप्रकार में मजबूत नहीं किया जा सकता" भाग के लिए एक उदाहरण था । इसलिए योग करने के लिए, एलएसपी का उल्लंघन करना संभवत: आपके कोड में कुछ बिंदु पर त्रुटियों का कारण होगा।


3

एलएसपी का कहना है कि '' वस्तुओं को उनके उपप्रकारों द्वारा बदली जानी चाहिए ''। दूसरी ओर, यह सिद्धांत इंगित करता है

चाइल्ड क्लासेस को पैरेंट क्लास की टाइप परिभाषाओं को कभी नहीं तोड़ना चाहिए।

और निम्न उदाहरण एलएसपी की बेहतर समझ रखने में मदद करता है।

एलएसपी के बिना:

public interface CustomerLayout{

    public void render();
}


public FreeCustomer implements CustomerLayout {
     ...
    @Override
    public void render(){
        //code
    }
}


public PremiumCustomer implements CustomerLayout{
    ...
    @Override
    public void render(){
        if(!hasSeenAd)
            return; //it isn`t rendered in this case
        //code
    }
}

public void renderView(CustomerLayout layout){
    layout.render();
}

एलएसपी द्वारा फिक्सिंग:

public interface CustomerLayout{
    public void render();
}


public FreeCustomer implements CustomerLayout {
     ...
    @Override
    public void render(){
        //code
    }
}


public PremiumCustomer implements CustomerLayout{
    ...
    @Override
    public void render(){
        if(!hasSeenAd)
            showAd();//it has a specific behavior based on its requirement
        //code
    }
}

public void renderView(CustomerLayout layout){
    layout.render();
}

2

मैं आपको लेख पढ़ने के लिए प्रोत्साहित करता हूं: लिज़कोव प्रतिस्थापन सिद्धांत (एलएसपी) का उल्लंघन

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


2

LISKOV SUBSTITUTION PRINCIPLE (मार्क सेमेन बुक से) कहता है कि हमें किसी क्लाइंट या तोड़े बिना किसी इंटरफ़ेस के एक कार्यान्वयन को बदलने में सक्षम होना चाहिए। यह सिद्धांत जो भविष्य में होने वाली आवश्यकताओं को पूरा करने में सक्षम बनाता है, भले ही हम ' टी आज उन्हें पसंद है।

यदि हम कंप्यूटर को दीवार (कार्यान्वयन) से अनप्लग करते हैं, तो न तो दीवार आउटलेट (इंटरफ़ेस) और न ही कंप्यूटर (क्लाइंट) टूट जाता है (वास्तव में, अगर यह लैपटॉप कंप्यूटर है, तो यह कुछ समय के लिए बैटरी पर भी चल सकता है) । सॉफ्टवेयर के साथ, हालांकि, एक ग्राहक अक्सर एक सेवा उपलब्ध होने की उम्मीद करता है। यदि सेवा हटा दी गई थी, तो हमें NullReferenceException मिलती है। इस प्रकार की स्थिति से निपटने के लिए, हम एक इंटरफ़ेस का कार्यान्वयन बना सकते हैं जो "कुछ भी नहीं" करता है। यह एक डिज़ाइन पैटर्न है जिसे नल ऑब्जेक्ट [4] के रूप में जाना जाता है और यह दीवार से कंप्यूटर को अनप्लग करने के लिए मोटे तौर पर मेल खाता है। क्योंकि हम ढीले युग्मन का उपयोग कर रहे हैं, हम वास्तविक कार्यान्वयन को किसी ऐसी चीज़ से बदल सकते हैं जो बिना परेशानी के कुछ भी नहीं करती है।


2

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

आशय - व्युत्पन्न प्रकारों को उनके आधार प्रकारों के लिए पूरी तरह से सक्षम होना चाहिए।

उदाहरण - जावा में सह-वैरिएंट वापसी प्रकार।


1

यहाँ इस पोस्ट से एक अंश है जो चीजों को अच्छी तरह से स्पष्ट करता है:

[..] कुछ सिद्धांतों को समझने के लिए, इसका उल्लंघन होने पर महसूस करना महत्वपूर्ण है। अब मैं यही करूंगा।

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

निम्नलिखित उदाहरण पर विचार करें:

interface Account
{
    /**
     * Withdraw $money amount from this account.
     *
     * @param Money $money
     * @return mixed
     */
    public function withdraw(Money $money);
}
class DefaultAccount implements Account
{
    private $balance;
    public function withdraw(Money $money)
    {
        if (!$this->enoughMoney($money)) {
            return;
        }
        $this->balance->subtract($money);
    }
}

क्या यह एलएसपी का उल्लंघन है? हाँ। ऐसा इसलिए है क्योंकि खाते का अनुबंध हमें बताता है कि एक खाता वापस ले लिया जाएगा, लेकिन यह हमेशा ऐसा नहीं होता है। तो, इसे ठीक करने के लिए मुझे क्या करना चाहिए? मैं सिर्फ अनुबंध को संशोधित करता हूं:

interface Account
{
    /**
     * Withdraw $money amount from this account if its balance is enough.
     * Otherwise do nothing.
     *
     * @param Money $money
     * @return mixed
     */
    public function withdraw(Money $money);
}

Voilà, अब अनुबंध संतुष्ट है।

यह सूक्ष्म उल्लंघन अक्सर एक ग्राहक को नियोजित ठोस वस्तुओं के बीच अंतर बताने की क्षमता के साथ लगाता है। उदाहरण के लिए, पहले खाते के अनुबंध को देखते हुए, यह निम्नलिखित की तरह लग सकता है:

class Client
{
    public function go(Account $account, Money $money)
    {
        if ($account instanceof DefaultAccount && !$account->hasEnoughMoney($money)) {
            return;
        }
        $account->withdraw($money);
    }
}

और, यह स्वचालित रूप से खुले-बंद सिद्धांत का उल्लंघन करता है [अर्थात, धन निकासी की आवश्यकता के लिए। क्योंकि आप कभी नहीं जानते कि क्या होता है यदि अनुबंध का उल्लंघन करने वाली वस्तु में पर्याप्त पैसा नहीं है। शायद यह सिर्फ कुछ नहीं लौटाता है, शायद एक अपवाद फेंक दिया जाएगा। तो आपको यह जांचना होगा कि क्या यह hasEnoughMoney()- जो एक इंटरफ़ेस का हिस्सा नहीं है। तो यह मजबूर ठोस-वर्ग पर निर्भर जांच एक OCP उल्लंघन है]।

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

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