क्या आप लिसकोव प्रतिस्थापन सिद्धांत (SOLID का 'L') एक अच्छे C # उदाहरण के साथ सिद्धांत के सभी पहलुओं को सरलीकृत तरीके से कवर कर सकते हैं? यदि यह वास्तव में संभव है।
क्या आप लिसकोव प्रतिस्थापन सिद्धांत (SOLID का 'L') एक अच्छे C # उदाहरण के साथ सिद्धांत के सभी पहलुओं को सरलीकृत तरीके से कवर कर सकते हैं? यदि यह वास्तव में संभव है।
जवाबों:
(यह उत्तर 2013-05-13 को फिर से लिखा गया है, टिप्पणियों के तल में चर्चा पढ़ें)
एलएसपी बेस क्लास के अनुबंध का पालन करने के बारे में है।
उदाहरण के लिए, आप उप-वर्गों में नए अपवादों को नहीं फेंक सकते क्योंकि आधार वर्ग का उपयोग करने वाले को यह उम्मीद नहीं होगी। ArgumentNullException
यदि कोई तर्क गुम है तो बेस क्लास फेंकता है और उप वर्ग तर्क को शून्य होने की अनुमति देता है, तो एलएसपी उल्लंघन भी करता है।
यहाँ एक वर्ग संरचना का एक उदाहरण है जो एलएसपी का उल्लंघन करता है:
public interface IDuck
{
void Swim();
// contract says that IsSwimming should be true if Swim has been called.
bool IsSwimming { get; }
}
public class OrganicDuck : IDuck
{
public void Swim()
{
//do something to swim
}
bool IsSwimming { get { /* return if the duck is swimming */ } }
}
public class ElectricDuck : IDuck
{
bool _isSwimming;
public void Swim()
{
if (!IsTurnedOn)
return;
_isSwimming = true;
//swim logic
}
bool IsSwimming { get { return _isSwimming; } }
}
और कॉलिंग कोड
void MakeDuckSwim(IDuck duck)
{
duck.Swim();
}
जैसा कि आप देख सकते हैं, बतख के दो उदाहरण हैं। एक ऑर्गेनिक डक और एक इलेक्ट्रिक डक। इलेक्ट्रिक डक केवल तभी तैर सकता है जब वह चालू हो। यह एलएसपी सिद्धांत को तोड़ता है क्योंकि इसे चालू करने में सक्षम होना चाहिए क्योंकि IsSwimming
आधार वर्ग के रूप में (जो अनुबंध का हिस्सा भी है) सेट नहीं किया जाएगा।
आप निश्चित रूप से कुछ ऐसा करके इसे हल कर सकते हैं
void MakeDuckSwim(IDuck duck)
{
if (duck is ElectricDuck)
((ElectricDuck)duck).TurnOn();
duck.Swim();
}
लेकिन यह ओपन / बंद सिद्धांत को तोड़ देगा और इसे हर जगह लागू किया जाएगा (और यह अभी भी अस्थिर कोड उत्पन्न करता है)।
उचित समाधान Swim
विधि में बत्तख को स्वचालित रूप से चालू करने के लिए होगा और ऐसा करने से विद्युत बत्तख ठीक वैसा ही व्यवहार करेगा जैसा कि IDuck
इंटरफ़ेस द्वारा परिभाषित किया गया है
अपडेट करें
किसी ने एक टिप्पणी जोड़ी और उसे हटा दिया। यह एक वैध बिंदु था जिसे मैं संबोधित करना चाहता हूं:
Swim
विधि के अंदर बत्तख को चालू करने के साथ समाधान वास्तविक कार्यान्वयन ( ElectricDuck
) के साथ काम करते समय दुष्प्रभाव हो सकता है । लेकिन यह एक स्पष्ट इंटरफ़ेस कार्यान्वयन का उपयोग करके हल किया जा सकता है । imho यह अधिक संभावना है कि आपको इसे चालू नहीं करने से समस्याएं आती हैं Swim
क्योंकि यह उम्मीद है कि IDuck
इंटरफ़ेस का उपयोग करते समय यह तैर जाएगा
अपडेट २
इसे और अधिक स्पष्ट करने के लिए कुछ हिस्सों को फिर से तैयार किया।
if duck is ElectricDuck
भाग की बात कर रहे हैं तो मेरे उत्तर को फिर से पढ़ें । मैंने पिछले गुरुवार को SOLID के बारे में एक सेमिनार किया :)
as
कीवर्ड के बारे में नहीं जानते हैं , जो वास्तव में उन्हें बहुत प्रकार की जाँच से बचाता है। मैं कुछ इस तरह सोच रहा हूं:if var electricDuck = duck as ElectricDuck; if(electricDuck != null) electricDuck.TurnOn();
if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness).
एलएसपी एक व्यावहारिक दृष्टिकोण
हर जगह मैं एलएसपी के सी # उदाहरणों की तलाश करता हूं, लोगों ने काल्पनिक कक्षाओं और इंटरफेस का उपयोग किया है। यहाँ एलएसपी का व्यावहारिक कार्यान्वयन है जिसे मैंने अपने सिस्टम में लागू किया है।
परिदृश्य: मान लीजिए कि हमारे पास 3 डेटाबेस (बंधक ग्राहक, चालू खाता ग्राहक और बचत खाता ग्राहक) हैं जो ग्राहक डेटा प्रदान करते हैं और हमें दिए गए ग्राहक के अंतिम नाम के लिए ग्राहक विवरण की आवश्यकता होती है। अब हम दिए गए अंतिम नाम के खिलाफ उन 3 डेटाबेस से 1 से अधिक ग्राहक विवरण प्राप्त कर सकते हैं।
कार्यान्वयन:
बिजनेस मॉडल लाइनर:
public class Customer
{
// customer detail properties...
}
डेटा पहुंच लाइन:
public interface IDataAccess
{
Customer GetDetails(string lastName);
}
उपरोक्त इंटरफ़ेस अमूर्त वर्ग द्वारा कार्यान्वित किया जाता है
public abstract class BaseDataAccess : IDataAccess
{
/// <summary> Enterprise library data block Database object. </summary>
public Database Database;
public Customer GetDetails(string lastName)
{
// use the database object to call the stored procedure to retrieve the customer details
}
}
इस सार वर्ग में सभी 3 डेटाबेसों के लिए एक सामान्य विधि "गेटडेलेट्स" है जो कि नीचे दिखाए गए डेटाबेस वर्गों में से प्रत्येक द्वारा बढ़ाया गया है
पोर्ट्रेट ग्राहक डेटा पहुंच:
public class MortgageCustomerDataAccess : BaseDataAccess
{
public MortgageCustomerDataAccess(IDatabaseFactory factory)
{
this.Database = factory.GetMortgageCustomerDatabase();
}
}
वर्तमान खाता ग्राहक डेटा पहुंच:
public class CurrentAccountCustomerDataAccess : BaseDataAccess
{
public CurrentAccountCustomerDataAccess(IDatabaseFactory factory)
{
this.Database = factory.GetCurrentAccountCustomerDatabase();
}
}
बचत खाता ग्राहक डेटा पहुंच:
public class SavingsAccountCustomerDataAccess : BaseDataAccess
{
public SavingsAccountCustomerDataAccess(IDatabaseFactory factory)
{
this.Database = factory.GetSavingsAccountCustomerDatabase();
}
}
एक बार ये 3 डेटा एक्सेस क्लासेस सेट हो जाने के बाद, अब हम क्लाइंट का ध्यान आकर्षित करते हैं। बिज़नेस लेयर में हमारे पास CustomerServiceManager वर्ग है जो अपने ग्राहकों को ग्राहक का विवरण देता है।
बिजनेस लाइन:
public class CustomerServiceManager : ICustomerServiceManager, BaseServiceManager
{
public IEnumerable<Customer> GetCustomerDetails(string lastName)
{
IEnumerable<IDataAccess> dataAccess = new List<IDataAccess>()
{
new MortgageCustomerDataAccess(new DatabaseFactory()),
new CurrentAccountCustomerDataAccess(new DatabaseFactory()),
new SavingsAccountCustomerDataAccess(new DatabaseFactory())
};
IList<Customer> customers = new List<Customer>();
foreach (IDataAccess nextDataAccess in dataAccess)
{
Customer customerDetail = nextDataAccess.GetDetails(lastName);
customers.Add(customerDetail);
}
return customers;
}
}
मैंने इसे पहले से जटिल होने के कारण इसे सरल रखने के लिए निर्भरता इंजेक्शन नहीं दिखाया है।
अब अगर हमारे पास एक नया ग्राहक विवरण डेटाबेस है, तो हम बस एक नया वर्ग जोड़ सकते हैं जो कि बेसडैटएक्सेस को बढ़ाता है और इसका डेटाबेस ऑब्जेक्ट प्रदान करता है।
बेशक हम सभी भाग लेने वाले डेटाबेस में समान संग्रहीत प्रक्रियाओं की आवश्यकता है।
अंत में, CustomerServiceManager
क्लास के लिए क्लाइंट केवल गेटकेंटरडेल विधि को कॉल करेगा, अंतिम नाम पास करेगा और यह ध्यान नहीं रखना चाहिए कि डेटा कैसे और कहां से आ रहा है।
आशा है कि यह आपको एलएसपी को समझने के लिए एक व्यावहारिक दृष्टिकोण देगा।
यहाँ Liskov सब्स्टीट्यूट सिद्धांत को लागू करने के लिए कोड है।
public abstract class Fruit
{
public abstract string GetColor();
}
public class Orange : Fruit
{
public override string GetColor()
{
return "Orange Color";
}
}
public class Apple : Fruit
{
public override string GetColor()
{
return "Red color";
}
}
class Program
{
static void Main(string[] args)
{
Fruit fruit = new Orange();
Console.WriteLine(fruit.GetColor());
fruit = new Apple();
Console.WriteLine(fruit.GetColor());
}
}
एलएसवी में कहा गया है: "व्युत्पन्न वर्गों को उनके आधार वर्गों (या इंटरफेस) के लिए प्रतिस्थापित किया जाना चाहिए" और "आधार कक्षाओं के संदर्भ (या इंटरफेस) का उपयोग करने वाले तरीकों को इसके बारे में जानने या विवरण के बिना व्युत्पन्न वर्गों के तरीकों का उपयोग करने में सक्षम होना चाहिए। । "