स्रोत : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/
C # एक अद्भुत भाषा है जो पिछले 14 वर्षों में परिपक्व और विकसित हुई है। यह हमारे लिए डेवलपर्स के लिए बहुत अच्छा है क्योंकि एक परिपक्व भाषा हमें भाषा सुविधाओं की अधिकता प्रदान करती है जो हमारे निपटान में हैं।
हालांकि, अधिक शक्ति के साथ बहुत जिम्मेदारी बन जाती है। इनमें से कुछ विशेषताओं का दुरुपयोग किया जा सकता है, या कभी-कभी यह समझना मुश्किल है कि आप एक से दूसरे फीचर का उपयोग क्यों करना चाहेंगे। इन वर्षों में, एक विशेषता जिसे मैंने कई डेवलपर्स संघर्षों के साथ देखा है, जब एक इंटरफ़ेस का उपयोग करने के लिए या एक अमूर्त वर्ग का उपयोग करने के लिए चुनना है। दोनों के फायदे और नुकसान हैं और प्रत्येक का उपयोग करने के लिए सही समय और स्थान है। लेकिन हम कैसे तय करें ???
दोनों प्रकार के बीच सामान्य कार्यक्षमता के पुन: उपयोग के लिए प्रदान करते हैं। सबसे स्पष्ट अंतर यह है कि इंटरफेस उनकी कार्यक्षमता के लिए कोई कार्यान्वयन प्रदान नहीं करते हैं, जबकि अमूर्त कक्षाएं आपको कुछ "आधार" या "डिफ़ॉल्ट" व्यवहार को लागू करने की अनुमति देती हैं और फिर आवश्यक हो तो वर्गों के साथ इस डिफ़ॉल्ट व्यवहार को "ओवरराइड" करने की क्षमता है। ।
यह सब अच्छी तरह से और अच्छा है और कोड के महान पुन: उपयोग के लिए प्रदान करता है और DRY (अपने आप को दोहराएं नहीं) सॉफ्टवेयर विकास के सिद्धांत का पालन करता है। जब आपके पास "संबंध" है तब सार कक्षाएं उपयोग करने के लिए महान हैं।
उदाहरण के लिए: एक गोल्डन रिट्रीवर "एक" कुत्ते का प्रकार है। तो एक पूडल है। वे दोनों भौंक सकते हैं, जैसा कि सभी कुत्ते कर सकते हैं। हालाँकि, आप यह बताना चाहेंगे कि पूडल पार्क "डिफ़ॉल्ट" कुत्ते की छाल से काफी अलग है। इस प्रकार, यह आपके लिए कुछ को लागू करने के लिए समझ में आ सकता है:
public abstract class Dog
{
public virtual void Bark()
{
Console.WriteLine("Base Class implementation of Bark");
}
}
public class GoldenRetriever : Dog
{
// the Bark method is inherited from the Dog class
}
public class Poodle : Dog
{
// here we are overriding the base functionality of Bark with our new implementation
// specific to the Poodle class
public override void Bark()
{
Console.WriteLine("Poodle's implementation of Bark");
}
}
// Add a list of dogs to a collection and call the bark method.
void Main()
{
var poodle = new Poodle();
var goldenRetriever = new GoldenRetriever();
var dogs = new List<Dog>();
dogs.Add(poodle);
dogs.Add(goldenRetriever);
foreach (var dog in dogs)
{
dog.Bark();
}
}
// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark
//
जैसा कि आप देख सकते हैं, यह आपके कोड DRY को रखने और बेस क्लास कार्यान्वयन के लिए अनुमति देने का एक शानदार तरीका होगा, जब किसी भी प्रकार के विशेष मामले के कार्यान्वयन के बजाय डिफ़ॉल्ट बार्क पर भरोसा कर सकते हैं। GoldenRetriever, Boxer, Lab जैसी कक्षाएं सभी "डिफ़ॉल्ट" (बास वर्ग) बार्क को बिना किसी शुल्क के विरासत में दे सकती थीं, क्योंकि वे डॉग अमूर्त वर्ग को लागू करती हैं।
लेकिन मुझे यकीन है कि आप पहले से ही जानते थे।
आप यहां हैं क्योंकि आप यह समझना चाहते हैं कि आप अमूर्त वर्ग या इसके विपरीत एक इंटरफ़ेस क्यों चुनना चाहते हैं। अच्छी तरह से एक कारण आप अमूर्त वर्ग पर एक इंटरफ़ेस चुनना चाहते हैं, जब आपके पास डिफ़ॉल्ट कार्यान्वयन को रोकने या न करने के लिए नहीं है। यह आमतौर पर इसलिए होता है क्योंकि इंटरफ़ेस को लागू करने वाले प्रकार "संबंध" में नहीं होते हैं। वास्तव में, उन्हें इस तथ्य को छोड़कर बिल्कुल भी संबंधित होने की आवश्यकता नहीं है कि प्रत्येक प्रकार "सक्षम" है या कुछ करने या कुछ करने के लिए "संक्षिप्तता" है।
अब इसका क्या मतलब है? ठीक है, उदाहरण के लिए: एक मानव एक बतख नहीं है ... और एक बतख मानव नहीं है। काफी सामान्य। हालांकि, एक बतख और एक मानव दोनों के पास तैरने की "क्षमता" है (यह देखते हुए कि मानव ने 1 ग्रेड में अपने तैराकी सबक पास किए :))। इसके अलावा, चूंकि एक बतख एक मानव या इसके विपरीत नहीं है, यह एक "अहसास" नहीं है, बल्कि एक "सक्षम" संबंध है और हम इसका वर्णन करने के लिए एक इंटरफ़ेस का उपयोग कर सकते हैं:
// Create ISwimable interface
public interface ISwimable
{
public void Swim();
}
// Have Human implement ISwimable Interface
public class Human : ISwimable
public void Swim()
{
//Human's implementation of Swim
Console.WriteLine("I'm a human swimming!");
}
// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
public void Swim()
{
// Duck's implementation of Swim
Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
}
}
//Now they can both be used in places where you just need an object that has the ability "to swim"
public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
somethingThatCanSwim.Swim();
}
public void Main()
{
var human = new Human();
var duck = new Duck();
var listOfThingsThatCanSwim = new List<ISwimable>();
listOfThingsThatCanSwim.Add(duck);
listOfThingsThatCanSwim.Add(human);
foreach (var something in listOfThingsThatCanSwim)
{
ShowHowYouSwim(something);
}
}
// So at runtime the correct implementation of something.Swim() will be called
// Output:
// Quack! Quack! I'm a Duck swimming!
// I'm a human swimming!
ऊपर दिए गए कोड की तरह इंटरफेस का उपयोग करने से आप किसी वस्तु को उस विधि से पारित कर सकते हैं जो कुछ करने के लिए "सक्षम" है। कोड यह परवाह नहीं करता है कि यह कैसे करता है ... यह सब जानता है कि यह उस वस्तु पर स्विम विधि को कॉल कर सकता है और उस वस्तु को पता चल जाएगा कि कौन सा व्यवहार उसके प्रकार के आधार पर रन-टाइम पर होता है।
एक बार फिर, यह आपके कोड को DRY में बने रहने में मदद करता है, ताकि आपको एक ही कोर फंक्शन (ShowHowHumanSwims (human), ShowHowDuckSwims (duck), आदि) को प्रीफॉर्म करने के लिए कई तरीके लिखने न पड़े।
यहां एक इंटरफ़ेस का उपयोग करने से कॉलिंग विधियों को यह चिंता करने की आवश्यकता नहीं है कि किस प्रकार का है या व्यवहार कैसे लागू किया गया है। यह सिर्फ जानता है कि इंटरफ़ेस को देखते हुए, प्रत्येक ऑब्जेक्ट को स्विम विधि को लागू करना होगा, इसलिए इसे अपने स्वयं के कोड में कॉल करना सुरक्षित है और स्विम विधि के व्यवहार को अपने स्वयं के वर्ग के भीतर संभालने की अनुमति है।
सारांश:
इसलिए मेरे अंगूठे का मुख्य नियम एक अमूर्त वर्ग का उपयोग करता है, जब आप किसी वर्ग पदानुक्रम या / और "या" के साथ काम कर रहे वर्गों और प्रकारों के लिए "डिफ़ॉल्ट" कार्यक्षमता को लागू करना चाहते हैं जो एक "संबंध" है (पूर्व। पूडल)। "कुत्ते का प्रकार)।
दूसरी तरफ एक इंटरफ़ेस का उपयोग करें जब आपके पास "एक" संबंध नहीं है, लेकिन ऐसे प्रकार हैं जो "कुछ करने या कुछ करने की क्षमता" को साझा करते हैं (उदा। बतख) एक मानव नहीं है। हालांकि, बतख और मानव शेयर। "तैरने की क्षमता"।
अमूर्त वर्गों और इंटरफेस के बीच ध्यान देने के लिए एक और अंतर यह है कि एक वर्ग एक से कई इंटरफेस को लागू कर सकता है लेकिन एक वर्ग केवल एक सार वर्ग (या उस मामले के लिए किसी भी वर्ग) से वारिस हो सकता है। हां, आप कक्षाओं को घोंसला बना सकते हैं और एक वंशानुगत पदानुक्रम (जो कई कार्यक्रम करते हैं और होना चाहिए) है, लेकिन आप एक व्युत्पन्न वर्ग परिभाषा में दो कक्षाएं नहीं ले सकते हैं (यह नियम C # पर लागू होता है। कुछ अन्य भाषाओं में आप ऐसा करने में सक्षम हैं। केवल इन भाषाओं में इंटरफेस की कमी के कारण)।
इंटरफ़ेस पृथक्करण सिद्धांत (ISP) का पालन करने के लिए इंटरफेस का उपयोग करते समय भी याद रखें। आईएसपी कहता है कि किसी भी ग्राहक को उन तरीकों पर निर्भर होने के लिए मजबूर नहीं किया जाना चाहिए जो इसका उपयोग नहीं करते हैं। इस कारण से इंटरफेस को विशिष्ट कार्यों पर ध्यान केंद्रित किया जाना चाहिए और आमतौर पर बहुत छोटा होता है (उदाहरण के लिए।
एक और टिप यह है कि यदि आप कार्यक्षमता के छोटे, संक्षिप्त बिट्स विकसित कर रहे हैं, तो इंटरफेस का उपयोग करें। यदि आप बड़ी कार्यात्मक इकाइयाँ डिज़ाइन कर रहे हैं, तो एक सार वर्ग का उपयोग करें।
आशा है कि यह कुछ लोगों के लिए चीजों को साफ करता है!
यदि आप किसी भी बेहतर उदाहरण के बारे में सोच सकते हैं या कुछ इंगित करना चाहते हैं, तो कृपया नीचे टिप्पणी में ऐसा करें!