व्युत्पन्न प्रकार अपने बेस प्रकार के लिए पूरी तरह से स्थानापन्न योग्य होने चाहिए।
परिभाषा
हमें चाइल्ड क्लास को ऐसे व्यवहार करना चाहिए जैसे वह पेरेंट क्लास हो। अनिवार्य रूप से इसका अर्थ है कि सभी व्युत्पन्न क्लासेस को अपने पेरेंट क्लास की कार्यक्षमता बरकरार रखनी चाहिए और पेरेंट द्वारा प्रदान की गई किसी भी कार्यक्षमता को प्रतिस्थापित नहीं करना चाहिए।
लिस्कोव सब्स्टीट्यूशन सिद्धांत लागू करने से पहले (उल्लंघन)
public class Ellipse
{
public double MajorAxis { get; set; }
public double MinorAxis { get; set; }
public virtual void SetMajorAxis(double majorAxis)
{
this.MajorAxis = majorAxis;
}
public virtual void SetMinorAxis(double minorAxis)
{
this.MinorAxis = minorAxis;
}
public virtual double Area()
{
return MajorAxis * MinorAxis * Math.PI;
}
}
public class Circle : Ellipse
{
public override void SetMajorAxis(double majorAxis)
{
base.SetMajorAxis(majorAxis);
this.MinorAxis = majorAxis; // सर्कल में, प्रत्येक एक्सिस समान होता है
}
}
public class Result
{
public void Method1()
{
Circle circle = new Circle();
circle.SetMajorAxis(5);
circle.SetMinorAxis(4); // बग! सर्कल में माइनर एक्सिस नहीं हो सकता
var area = circle.Area(); // 5*4 = 20, लेकिन हमने 5*5 = 25 की अपेक्षा की थी
}
}
समस्या: Circle, Ellipse का व्यवहार नहीं बदल सकता। SetMinorAxis को कॉल करने से सर्कल की अनिवार्यता टूट जाती है (दोनों अक्ष समान होने चाहिए)।
लिस्कोव सब्स्टीट्यूशन सिद्धांत लागू करने के बाद (अनुपालन)
internal class Program
{
private static void Main() { }
}
// विकल्प 1: Ellipse को बेस बनाएं लेकिन Circle के लिए म्यूटेटर्स को प्रतिबंधित करें
public class Ellipse
{
public double MajorAxis { get; set; }
public double MinorAxis { get; set; }
public virtual void SetMajorAxis(double majorAxis) { MajorAxis = majorAxis; }
public virtual void SetMinorAxis(double minorAxis) { MinorAxis = minorAxis; }
public virtual double Area() { return MajorAxis * MinorAxis * Math.PI; }
}
public class Circle : Ellipse
{
public override void SetMajorAxis(double majorAxis)
{
base.SetMajorAxis(majorAxis);
this.MinorAxis = majorAxis;
}
public override void SetMinorAxis(double minorAxis)
{
base.SetMinorAxis(minorAxis);
this.MajorAxis = minorAxis;
}
public override double Area() { return base.Area(); }
}
// विकल्प 2: बेहतर - सामान्य बेस से अलग क्लासेस (कंपोजिशन ओवर इनहेरिटेंस)
public class Circle1
{
public double Radius { get; set; }
public void SetRadius(double radius) { this.Radius = radius; }
public double Area() { return this.Radius * this.Radius * Math.PI; }
}
LSP के नियम (अनुबंध)
| नियम | विवरण |
|---|---|
| प्रीकंडीशन | व्युत्पन्न क्लास में कमजोर नहीं हो सकते (कम सख्त) |
| पोस्टकंडीशन | व्युत्पन्न क्लास में मजबूत नहीं हो सकते (अधिक सख्त) |
| इनवेरिएंट्स | बेस क्लास के इनवेरिएंट्स संरक्षित होने चाहिए |
| इतिहास बाधा | बेस क्लास की अपरिवर्तनीय स्थिति व्युत्पन्न में भी अपरिवर्तनीय रहनी चाहिए |
LSP उल्लंघन के संकेत
- व्युत्पन्न क्लास में बेस मेथड को खाली कार्यान्वयन या अपवाद के साथ ओवरराइड किया जाता है
- क्लाइंट कोड ऑब्जेक्ट के वास्तविक प्रकार की जांच करता है (typeof, is, as)
- व्युत्पन्न क्लास को बेस क्लास के व्यवहार को "अनडू" करना पड़ता है
- "is-a" संबंध व्यवहारिक रूप से मान्य नहीं है
समाधान रणनीतियाँ
- इंटरफेस सेग्रिगेशन: व्यवहार को छोटे इंटरफेस में विभाजित करें
- कंपोजिशन: विरासत के बजाय हैज़-ए संबंधों का उपयोग करें
- टेम्पलेट मेथड: बेस में एल्गोरिदम संरचना परिभाषित करें, हुक्स को ओवरराइड करने दें
- फैक्टरी: उपयुक्त ठोस प्रकार बनाने के लिए फैक्टरी का उपयोग करें
मुख्य अंतर्दृष्टि
"यदि S, T का सबटाइप है, तो T प्रकार के ऑब्जेक्ट्स को S प्रकार के ऑब्जेक्ट्स से प्रतिस्थापित किया जा सकता है बिना प्रोग्राम की वांछनीय संपत्तियों को बदले।" - बारबरा लिस्कोव