2016-02-12 5 views
11

इस विषय पर "पुन: उपयोग ऑब्जेक्ट संरचना उत्तराधिकारी" विषय पर a great video द्वारा प्रेरित किया गया है जो जावास्क्रिप्ट उदाहरणों का उपयोग करता है; मैं अवधारणा की मेरी समझ का परीक्षण करने के लिए इसे सी # में देखना चाहता था, लेकिन यह भी नहीं चला था जैसा कि मैंने आशा की थी।इस इंटरफ़ेस बनाम इनहेरिटेंस सी # उदाहरण

/// PREMISE 
// Animal base class, Animal can eat 
public class Animal 
{ 
    public void Eat() { } 
} 
// Dog inherits from Animal and can eat and bark 
public class Dog : Animal 
{ 
    public void Bark() { Console.WriteLine("Bark"); } 
} 
// Cat inherits from Animal and can eat and meow 
public class Cat : Animal 
{ 
    public void Meow() { Console.WriteLine("Meow"); } 
} 
// Robot base class, Robot can drive 
public class Robot 
{ 
    public void Drive() { } 
} 

समस्या यह है कि मैं RobotDog वर्ग कि छाल और ड्राइव कर सकते हैं, लेकिन नहीं खाने जोड़ना चाहते है।


पहले समाधान रोबोट के उपवर्ग के रूप में RobotDog बनाने के लिए,

public class RobotDog : Robot 
{ 
    public void Bark() { Console.WriteLine("Bark"); } 
} 

लेकिन यह एक बार्क समारोह हम कॉपी और इसलिए अब हम डुप्लिकेट कोड है कुत्ते की छाल समारोह पेस्ट करना पड़ा देने के लिए किया गया था।


दूसरा समाधान एक बार्क विधि है कि फिर दोनों पशु और रोबोट वर्गों से

public class WorldObject 
{ 
    public void Bark() { Console.WriteLine("Bark"); } 
} 
public class Animal : WorldObject { ... } 
public class Robot : WorldObject { ... } 

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


तीसरे समाधान है कि

public interface IBarkable 
{ 
    void Bark(); 
} 

छाल और कुत्ता और RobotDog कक्षाएं

public class Dog : Animal, IBarkable 
{ 
    public void IBarkable.Bark() { Console.WriteLine("Bark"); } 
} 
public class RobotDog : Robot, IBarkable 
{ 
    public void IBarkable.Bark() { Console.WriteLine("Bark"); } 
} 

लेकिन एक बार फिर से हम डुप्लिकेट है में इसे लागू कर सकते हैं कक्षाओं के लिए एक IBarkable इंटरफ़ेस का निर्माण करना था कोड!


चौथे विधि एक बार फिर से IBarkable इंटरफेस का उपयोग करने के लिए, लेकिन एक बार्क सहायक वर्ग है कि तब कुत्ता और RobotDog इंटरफेस कार्यान्वयन में से प्रत्येक में फोन बनाने था।

यह सबसे अच्छा तरीका (और वीडियो क्या अनुशंसा करता है) की तरह लगता है, लेकिन मैं प्रोजेक्टर से अव्यवस्थित होने वाली परियोजना से भी एक समस्या देख सकता हूं।


एक पांचवें सुझाव (hacky?) इतना है कि अगर आप IBarkable लागू है, तो आप

public interface IBarker { } 
public static class ExtensionMethods 
{ 
    public static void Bark(this IBarker barker) { 
     Console.WriteLine("Woof!"); 
    } 
} 

एक बहुत छाल कर सकते हैं समाधान, एक खाली IBarkable इंटरफ़ेस एक विस्तार विधि बंद लटका था इस साइट पर समान उत्तर दिए गए प्रश्नों के साथ-साथ जिन लेखों को मैंने पढ़ा है, वे abstract कक्षाओं का उपयोग करने की अनुशंसा करते हैं, हालांकि, क्या समाधान 2 के समान समस्याएं नहीं होंगी?


सबसे अच्छा वस्तु उन्मुख इस उदाहरण के लिए RobotDog वर्ग को जोड़ने के लिए तरीका क्या है?

+1

आपके उदाहरण में, जिस तरह से पशु छाल या रोबोट छाल बदलते नहीं हैं, जो गलत लगता है .... बार्क व्यवहार दोनों मामलों में अलग होगा जिसका मतलब है कि छाल विधि में जो कोड आप लिखते हैं वह अलग होगा .... – Viru

+0

@ वीरू मैं कहूंगा कि प्रश्न में सटीक कार्य केवल चित्रकारी उद्देश्यों के लिए हैं और सचमुच नहीं लिया जाना चाहिए। इसके अलावा, मुझे यकीन है कि वहां रोबोट कुत्ते हैं जो छाल हैं। – user1666620

+0

मुझे लगता है कि आपका केस एक समग्र पैटर्न का एक आदर्श उदाहरण है। "विरासत पर रचना का पक्ष लें": https://www.safaribooksonline.com/library/view/head-first-design/0596007124/ch01.html –

उत्तर

3

सबसे पहले यदि आप "विरासत पर संरचना" का पालन करना चाहते हैं तो आपके समाधानों में से आधे से अधिक फिट नहीं होते हैं क्योंकि आप अभी भी उन लोगों में विरासत का उपयोग करते हैं।

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

class RobotDog 
    implements interface IEat, IBark 
    implements mixin MEat, MBark 

IEat और IBark, इंटरफेस प्रदान करता है, जबकि MEat और MBark कुछ डिफ़ॉल्ट कार्यान्वयन है कि आप इंजेक्षन सकता है के साथ mixins होगा इस तरह एक वर्ग हो सकता था। इस तरह की एक डिजाइन जावास्क्रिप्ट में संभव है, लेकिन वर्तमान में सी # में नहीं है। इसका लाभ यह है कि अंत में आपके पास RobotDog कक्षा है जिसमें साझा किए गए कार्यान्वयन के साथ IEat और IBark के सभी तरीके हैं। और यह भी एक ही समय में एक नुकसान है, क्योंकि आप कई विधियों के साथ बड़ी कक्षाएं बनाते हैं। इसके शीर्ष पर विधि विवाद हो सकते हैं। उदाहरण के लिए जब आप एक ही नाम/हस्ताक्षर के साथ दो अलग-अलग इंटरफेस इंजेक्ट करना चाहते हैं। इस तरह के दृष्टिकोण के रूप में अच्छा लगता है, मुझे लगता है कि नुकसान बड़े हैं और मैं इस तरह के एक डिजाइन को प्रोत्साहित नहीं करता।


के रूप में सी # mixins का समर्थन नहीं करता सीधे आप एक्सटेंशन के तरीके इस्तेमाल कर सकते हैं किसी भी तरह से ऊपर डिजाइन फिर से बनाया है। तो आपके पास अभी भी IEat और IBark इंटरफेस हैं। और आप इंटरफेस के लिए एक्सटेंशन विधियां प्रदान करते हैं। लेकिन यह मिश्रण मिश्रण के रूप में एक ही नुकसान है। ऑब्जेक्ट पर सभी विधियां दिखाई देती हैं, विधि नाम टक्कर वाली समस्याएं। शीर्ष पर भी, रचना का विचार यह भी है कि आप विभिन्न कार्यान्वयन प्रदान कर सकते हैं। आप एक ही इंटरफेस के लिए अलग मिक्सिन भी हो सकते हैं। और इसके शीर्ष पर, मिश्रण कुछ प्रकार के डिफ़ॉल्ट कार्यान्वयन के लिए हैं, विचार अभी भी है कि आप एक विधि को ओवरराइट या बदल सकते हैं।

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


सामान्य तरीके से मैं इसे कैसे हल करूंगा, यह आपके इच्छित व्यवहार के लिए फ़ील्ड की अपेक्षा कर रहा है।तो आपका रोबोटडॉग इस तरह दिखता है

class RobotDog(ieat, ibark) 
    IEat Eat = ieat 
    IBark Bark = ibark 

तो इसका मतलब है। आपके पास एक कक्षा है जिसमें दो गुण Eat और Bark शामिल हैं। वे गुण IEat और IBark प्रकार हैं। यदि आप RobotDog उदाहरण बनाना चाहते हैं तो आपको एक विशिष्ट IEat और IBark कार्यान्वयन में प्रवेश करना होगा जिसका आप उपयोग करना चाहते हैं।

let eat = new CatEat() 
let bark = new DogBark() 
let robotdog = new RobotDog(eat, bark) 

अब रोबोटडॉग एक बिल्ली की तरह खाएगा, और छाल की तरह छाल खाएगा। आप बस कॉल कर सकते हैं कि आपके रोबोटडॉग को क्या करना चाहिए।

robotdog.Eat.Fruit() 
robotdof.Eat.Drink() 
robotdog.Bark.Loud() 

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

robotdog.Bark <- new DeadlyScreamBarking() 

किसी भी तरह से करना चाहते हैं। आप एक परिवर्तनीय या अपरिवर्तनीय डिजाइन का उपयोग कर सकते हैं, यह आपके ऊपर है। तो आपके पास कोड साझा करना है। कम से कम मुझे शैली बहुत पसंद है, क्योंकि सैकड़ों तरीकों से ऑब्जेक्ट रखने की बजाय मूल रूप से अलग-अलग ऑब्जेक्ट्स वाली पहली परत होती है जिसमें प्रत्येक क्षमता को अलग-अलग अलग किया जाता है। उदाहरण के लिए यदि आप अपनी रोबोटडॉग कक्षा में स्थानांतरित करना जोड़ते हैं तो आप केवल "अचल" संपत्ति जोड़ सकते हैं और उस इंटरफ़ेस में MoveTo, CalculatePath, Forward, SetSpeed और अन्य कई विधियां हो सकती हैं। वे robotdog.Move.XYZ के तहत स्पष्ट रूप से उपलब्ध होंगे। आपको टकराव विधियों के साथ कोई समस्या नहीं है। उदाहरण के लिए बिना किसी समस्या के प्रत्येक वर्ग पर एक ही नाम के साथ विधियां हो सकती हैं। और शीर्ष पर। आप भी एक ही प्रकार के कई व्यवहार कर सकते हैं! उदाहरण के लिए स्वास्थ्य और शील्ड एक ही प्रकार का उपयोग कर सकते हैं। उदाहरण के लिए एक साधारण "मिनमैक्स" प्रकार जिसमें न्यूनतम/अधिकतम और वर्तमान मूल्य और उन पर काम करने के तरीके शामिल हैं। स्वास्थ्य/शील्ड मूल रूप से वही व्यवहार करते हैं, और आप आसानी से इस दृष्टिकोण के साथ एक ही कक्षा में उनमें से दो का उपयोग कर सकते हैं क्योंकि कोई विधि/संपत्ति या घटना टकराव नहीं कर रही है।

robotdog.Health.Increase(10) 
robotdog.Shield.Increase(10) 

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

robotdog.Eat.Fruit() 
robotdog.Eat.Drink() 

के बजाय आपको लगता है कि खाने के मैदान पर कुछ प्रणाली को बुलाती है, तो साथ क्या आप अंत किया RobotDog पर तरीकों को लागू?

robotdog.EatFruit() 
robotdog.EatDrink() 

तुम भी तरह

robotdog.IncreaseHealt(10) 
robotdog.IncreaseShield(10) 

टकराव को हल करने के लिए एक बार फिर जरूरत है वास्तव में आप सिर्फ तरीकों का एक बहुत लिखते हैं कि सिर्फ एक मैदान पर कुछ अन्य तरीकों के प्रतिनिधियों। लेकिन आपने क्या जीता? असल में कुछ भी नहीं। आपने बस एक नियम दिमाग का पालन किया। आप सैद्धांतिक रूप से कह सकते हैं। लेकिन कुछ अलग कर सकता है या Eat.Fruit() पर कॉल करने से पहले कुछ अतिरिक्त कर सकता है।वील हाँ हो सकता है। लेकिन यदि आप अन्य अलग-अलग भोजन व्यवहार चाहते हैं तो आप केवल IEat लागू करने वाली एक और कक्षा बनाते हैं और जब आप इसे तुरंत चालू करते हैं तो आप उस वर्ग को रोबोटडॉग को असाइन करते हैं।

उस अर्थ में, डेमेटर का कानून व्यायाम की गिनती नहीं है।

http://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx/


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

+0

अब तक, ऐसा लगता है जैसे encapsulation जाने का तरीका है। – chriszumberge

2

मुझे लगता है कि यह एक रचना मुद्दे के बजाय एक वैचारिक दुविधा है।

जब आप कहते हैं: और कुत्ता और RobotDog कक्षाएं

public class Dog : Animal, IBarkable 
{ 
    public void IBarkable.Bark() { Console.WriteLine("Bark"); } 
} 
public class RobotDog : Robot, IBarkable 
{ 
    public void IBarkable.Bark() { Console.WriteLine("Bark"); } 
} 

में इसे लागू लेकिन एक बार फिर से हम डुप्लिकेट कोड है!

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

+0

पशु वर्ग से रोबोटडॉग प्राप्त करना वास्तव में समाधान नहीं हो सकता है क्योंकि तब हमें एक ही समस्या है। अगर हमने ऐसा किया है, तो अब रोबोटडॉग में पशु वर्ग से एक ईट() विधि है जो उसके पास नहीं होनी चाहिए। इसके अतिरिक्त अब रोबोट कक्षा से ड्राइव() विधि की कमी है। अब हमें ड्राइव() विधि को रोबोटडॉग क्लास (प्रतिलिपि/पेस्ट डुप्लिकेट कोड) में कॉपी करने या एक आईडीआरएबल इंटरफेस को लागू करने की आवश्यकता होगी (कक्षाओं के बीच डुप्लिकेट समान कार्यान्वयन के साथ आईबीर्कबल के समान मुद्दे) – chriszumberge

+2

विरासत का मुख्य उद्देश्य कोड- साझा करने। "हाई दो कक्षाओं के उन हास्यास्पद विचारों का उपयोग उसी विधि का उपयोग करते हैं, कक्षा में डाल दिया जाता है और इससे प्राप्त होता है" सटीक भयानक विरासत पेड़ की ओर जाता है जो कि किसी भी समय वास्तव में किसी को भी समझ नहीं सकता है। विरासत उपप्रकार बनाने के लिए है, कि वे साझा कोड साझा कर सकते हैं "अच्छा होना" है, आप कोड की कुछ पंक्तियों को साझा करने के उद्देश्य से प्राप्त नहीं करते हैं। आपको एलएसपी का पालन करना चाहिए। और सर्किल-एलीप्स समस्या यह बताती है कि आपको कुछ कोड साझा करने के लिए क्यों उत्तराधिकारी नहीं होना चाहिए: https://en.wikipedia.org/wiki/Circle-ellipse_problem –

+1

@chriszumberge मुझे लगता है कि मुद्दा यह है कि आप दो को गठबंधन करने की कोशिश कर रहे हैं विभिन्न अवधारणाएं पशु और रोबोट। रोबोटडॉग और कुत्ते एक ही तरह से छाल नहीं करते हैं और अनिवार्य रूप से दो अलग-अलग वस्तुएं हैं जो "भौंकने योग्य" व्यवहार के बावजूद हैं। इस बात से सहमत? – mojorisinify

संबंधित मुद्दे