मूल रूप से हम चीजों को समझदारी से व्यवहार करना चाहते हैं।
निम्नलिखित समस्या पर विचार करें:
मुझे आयतों का एक समूह दिया गया है और मैं उनका क्षेत्र 10% बढ़ाना चाहता हूँ। इसलिए मैं जो कुछ करता हूं, वह आयत की लंबाई 1.1 गुना करने के लिए सेट किया गया है जो पहले था।
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles)
{
foreach(var rectangle in rectangles)
{
rectangle.Length = rectangle.Length * 1.1;
}
}
अब इस मामले में, मेरी सभी आयतों में अब उनकी लंबाई 10% बढ़ गई है, जिससे उनके क्षेत्र में 10% की वृद्धि होगी। दुर्भाग्य से, किसी ने वास्तव में मुझे वर्गों और आयतों का मिश्रण पारित किया है, और जब आयत की लंबाई बदल दी गई थी, तो चौड़ाई थी।
मेरी इकाई परीक्षण पास हो जाते हैं क्योंकि मैंने अपने सभी इकाई परीक्षणों को आयतों के संग्रह का उपयोग करने के लिए लिखा था। मैंने अब अपने आवेदन में एक सूक्ष्म बग पेश किया है जो महीनों तक किसी का ध्यान नहीं जा सकता है।
इससे भी बुरी बात यह है कि लेखांकन से जिम मेरे तरीके को देखता है और कुछ अन्य कोड लिखता है जो इस तथ्य का उपयोग करता है कि यदि वह मेरी विधि में वर्गों को पारित करता है, तो उसे आकार में बहुत अच्छा 21% वृद्धि मिलती है। जिम खुश है और कोई भी समझदार नहीं है।
जिम को एक अलग विभाग में उत्कृष्ट कार्य के लिए पदोन्नत किया जाता है। अल्फ्रेड कंपनी में जूनियर के रूप में शामिल होते हैं। अपनी पहली बग रिपोर्ट में, विज्ञापन से जिल ने बताया है कि इस पद्धति के लिए वर्गों को पारित करने के परिणामस्वरूप 21% की वृद्धि हुई है और बग को ठीक करना चाहता है। अल्फ्रेड देखता है कि चौकों और आयतों को कोड में हर जगह इस्तेमाल किया जाता है और पता चलता है कि वंशानुक्रम श्रृंखला को तोड़ना असंभव है। उसके पास लेखांकन के स्रोत कोड तक पहुंच भी नहीं है। तो अल्फ्रेड इस तरह बग को ठीक करता है:
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles)
{
foreach(var rectangle in rectangles)
{
if (typeof(rectangle) == Rectangle)
{
rectangle.Length = rectangle.Length * 1.1;
}
if (typeof(rectangle) == Square)
{
rectangle.Length = rectangle.Length * 1.04880884817;
}
}
}
अल्फ्रेड अपने uber हैकिंग कौशल के साथ खुश है और जिल बंद बग तय है कि संकेत।
अगले महीने किसी को भुगतान नहीं किया जाता है क्योंकि लेखांकन IncreaseRectangleSizeByTenPercent
विधि में वर्गों को पारित करने में सक्षम होने और 21% के क्षेत्र में वृद्धि प्राप्त करने पर निर्भर था । समस्या के स्रोत को ट्रैक करने के लिए पूरी कंपनी "प्राथमिकता 1 बगफिक्स" मोड में जाती है। वे अल्फ्रेड को ठीक करने के लिए समस्या का पता लगाते हैं। वे जानते हैं कि उन्हें लेखांकन और विज्ञापन दोनों को खुश रखना होगा। तो वे विधि कॉल के साथ उपयोगकर्ता की पहचान करके समस्या को ठीक करते हैं:
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles)
{
IncreaseRectangleSizeByTenPercent(
rectangles,
new User() { Department = Department.Accounting });
}
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles, User user)
{
foreach(var rectangle in rectangles)
{
if (typeof(rectangle) == Rectangle || user.Department == Department.Accounting)
{
rectangle.Length = rectangle.Length * 1.1;
}
else if (typeof(rectangle) == Square)
{
rectangle.Length = rectangle.Length * 1.04880884817;
}
}
}
इत्यादि इत्यादि।
यह किस्सा वास्तविक दुनिया की स्थितियों पर आधारित है जो रोजाना प्रोग्रामर का सामना करते हैं। लिस्कोव प्रतिस्थापन सिद्धांत का उल्लंघन बहुत ही सूक्ष्म कीड़े को पेश कर सकता है जो केवल लिखे जाने के वर्षों बाद मिलते हैं, जिस समय तक उल्लंघन को ठीक करने से चीजों का एक गुच्छा टूट जाएगा और इसे ठीक नहीं करना आपके सबसे बड़े ग्राहक को गुस्सा दिलाएगा।
इस समस्या को ठीक करने के दो यथार्थवादी तरीके हैं।
पहला तरीका आयत को अपरिवर्तनीय बनाना है। यदि आयत का उपयोगकर्ता लंबाई और चौड़ाई गुण नहीं बदल सकता है, तो यह समस्या दूर हो जाती है। यदि आप एक अलग लंबाई और चौड़ाई के साथ एक आयत चाहते हैं, तो आप एक नया बनाते हैं। वर्गों खुशी से आयतों से विरासत में मिल सकता है।
दूसरा तरीका वर्गों और आयतों के बीच विरासत श्रृंखला को तोड़ने का है। एक वर्ग के लिए एक एकल के रूप में परिभाषित किया जाता है तो SideLength
संपत्ति और आयतों एक है Length
और Width
संपत्ति और वहाँ कोई विरासत है, यह गलती से एक आयत की उम्मीद है और एक वर्ग हो रही द्वारा बातें तोड़ने के लिए असंभव है। C # शब्दों में, आप seal
अपना आयत वर्ग बना सकते हैं, जो यह सुनिश्चित करता है कि आपके द्वारा प्राप्त किए गए सभी आयत वास्तव में Rectangles हैं।
इस मामले में, मुझे समस्या को ठीक करने का "अपरिवर्तनीय वस्तुएं" तरीका पसंद है। एक आयत की पहचान इसकी लंबाई और चौड़ाई है। यह समझ में आता है कि जब आप किसी वस्तु की पहचान बदलना चाहते हैं, तो आप वास्तव में जो चाहते हैं, वह एक नई वस्तु है। यदि आप एक पुराने ग्राहक को खो देते हैं और एक नया ग्राहक प्राप्त करते हैं, तो आप Customer.Id
पुराने ग्राहक से नए क्षेत्र में परिवर्तन नहीं करते हैं , आप एक नया बनाते हैं Customer
।
Liskov प्रतिस्थापन सिद्धांत के उल्लंघन वास्तविक दुनिया में आम हैं, ज्यादातर इसलिए क्योंकि वहां बहुत से कोड ऐसे लोगों द्वारा लिखे गए हैं जो अक्षम / समय के दबाव में हैं / गलतियों की परवाह नहीं करते / करते हैं। यह कुछ बहुत ही खराब समस्याओं को जन्म दे सकता है। ज्यादातर मामलों में, आप इसके बजाय विरासत पर रचना का पक्ष लेना चाहते हैं ।