2010-08-23 7 views
7

मेरे पास एक डिज़ाइन समस्या है जिसे मैं हल करना चाहता हूं। मेरे पास एक इंटरफ़ेस है, इसे IProtocol पर कॉल करें, जिसे दो अलग-अलग वर्गों द्वारा कार्यान्वित किया जाता है। हम यहाँ कोड की 600 से अधिक लाइनों को देख रहे हैं।डिज़ाइन समस्या - विरासत इस कोड को सरल बनाने का सही तरीका है?

public class Protocol1 : IProtocol 
{ 
    MyInterfaceMethod1() 
    { 
    Same1(); 
    DiffStuff(); 
    Same2(); 
    } 
} 

और

public class Protocol2 : IProtocol 
{ 
    MyInterfaceMethod1() 
    { 
    Same1(); 
    Same2(); 
    } 
} 
: सामान वे करते हैं के विशाल बहुमत, कुछ विशिष्ट क्षेत्रों के लिए को छोड़कर, एक ही है DiffStuff();

वर्तमान संरचना की तरह कुछ इस तरह है

मैं होने कॉपी-पेस्ट त्रुटियों और टी के साथ चिंतित हूँ यदि मैं दो प्रोटोकॉल अलग रखता हूं तो वह कोड डुप्लिकेशन का क्लासिक समस्या है। हम प्रत्येक कोड की पूर्ण 600 लाइनों के बारे में बात कर रहे हैं, कुछ सरल तरीकों से नहीं।

मैं (Protocol2 ज्यादातर एक ही रहने को छोड़कर मैं निजी तरीकों में Same1() और Same2() रैप करने के लिए होगा होगा।)

public class Protocol1 : Protocol2 
{ 
    void Same1() 
    { 
    base.Same1(); 
    } 

    void Same2() 
    { 
    base.Same2(); 
    } 

    MyInterfaceMethod1() 
    { 
    Same1(); 
    DiffStuff(); 
    Same2(); 
    } 
} 

Protocol1 के कार्यान्वयन को बदलने protocol2 से प्राप्त करना इस तरह, पर विचार कर रहा हूँ क्या इस समस्या से निपटने के लिए यह सही तरीका है?

संपादित करें: कई लोगों ने मुझे इस प्रश्न के साथ मदद की, स्पष्ट समझ के लिए धन्यवाद। मेरे मामले में, दोनों ऑब्जेक्ट्स एक ही प्रकार के नहीं हैं, भले ही उनके अधिकांश कार्यान्वयन को साझा किया गया हो, इसलिए मैं कक्षाओं के बीच परिवर्तनों को समाहित करने के लिए छोटे तरीकों का निर्माण करने के लिए अमूर्त बेस क्लास का उपयोग करने के लिए Bobby's suggestion के साथ गया। करने के लिए अतिरिक्त धन्यवाद:

  • jloubert
  • हंस Passant
  • जेफ स्टर्नल
+10

एक अमूर्त वर्ग है कि आवश्यक जो कि लागू नहीं किया जाता है के लिए साझा तरीकों और अमूर्त परिभाषाओं को परिभाषित करता है क्यों उपयोग नहीं ? – alternative

+0

रॉबेट सी मार्टिन –

+2

द्वारा "क्लीन कोड" पर नज़र डालें, यदि आपको वास्तव में समझ में आता है तो आपको केवल 'प्रोटोकॉल 1' से 'प्रोटोकॉल 1' प्राप्त करना चाहिए। आपको पूछने की जरूरत है, "क्या हर प्रोटोकॉल 1 प्रोटोकॉल 2 का एक उदाहरण भी है, उसी अर्थ में कि हर बिल्ली एक स्तनधारी है?" यदि वे हैं, तो आप जो सोच रहे हैं वह करें। यदि नहीं, तो बॉबी के जवाब में दिखाए गए अनुसार, मैं एक सार आधार श्रेणी के साथ जाऊंगा। – jloubert

उत्तर

7
/// <summary> 
    /// IProtocol interface 
    /// </summary> 
    public interface IProtocol 
    { 
     void MyInterfaceMethod1(); 
     void Same1(); 
     void Same2(); 
    } 
तो

...

public abstract class ProtocolBase : IProtocol 
{ 
    #region IProtocol Members 

    public void MyInterfaceMethod1() 
    { 
     // Implementation elided... 
    } 

    public void Same1() 
    { 
     // Implementation elided... 
    } 

    public void Same2() 
    { 
     // Implementation elided... 
    } 

    public abstract void DiffStuff(); 

    #endregion 
} 
अंत में

...

public sealed class Protocol1 : ProtocolBase 
{ 
    public override void DiffStuff() 
    { 
     // Implementation elided... 
    } 
} 

public sealed class Protocol2 : ProtocolBase 
{ 
    public override void DiffStuff() 
    { 
     // Implementation elided... 
    } 
} 
5

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

-1

मैं मैथएपिक से सहमत हूं। मैं Template method पैटर्न का उपयोग करूंगा।

+3

(-1) कृपया अपनी टिप्पणियां * टिप्पणियां * के रूप में पोस्ट करें। – DevinB

0

मैं बेहतर डिजाइन (मेरी राय में)

public abstract class Protocol_common : IProtocol 
{ 
    MyInterfaceMethod1() 
    { 
    Same1(); 
    DiffStuff(); 
    Same2(); 
    } 

    abstract void DiffStuff(); 

} 

public class Protocol1 : Protocol_common 
{ 
    DiffStuff() 
    { 
     /// stuff here. 
    } 
} 

public class Protocol2 : Protocol_common 
{ 
    DiffStuff() 
    { 
     /// nothing here. 
    } 
} 

(यही कारण है कि तुलना में उचित सी # वास्तव में अधिक छद्म कोड है, लेकिन मैं उच्च अंक हिट)

+1

प्रोटोकॉल 1 और प्रोटोकॉल_कॉमन और प्रोटोकॉल_कॉमॉन से उत्तराधिकारी होना चाहिए अमूर्त होना चाहिए? –

+0

डी ओह! बहुत कॉपी 'n'paste –

+0

प्रोटोकॉल 1 और प्रोटोकॉल 2 के लिए MyInterfaceMethod1 का कार्यान्वयन थोड़ा अलग है (प्रोटोकॉल 2 वास्तव में डिफस्टफ को कॉल नहीं करता है), इसलिए इसे सारणी बनाने के लिए अलग-अलग किया जाना चाहिए और प्रति व्युत्पन्न वर्ग को अलग से लागू किया जाना चाहिए। या शायद आभासी तरीकों का उपयोग करें और डिफ़ॉल्ट व्यवहार होने के लिए डिफस्टफ कार्यान्वयन में से एक पर विचार करें। – fletcher

4

आप बहुत template method pattern का वर्णन करने ही वाले हैं , जो आपके जैसे समस्याओं के प्रभावी समाधान के रूप में एक लंबी वंशावली है।

हालांकि, आपको विरासत की बजाय संरचना का उपयोग करने पर विचार करना चाहिए। The two approaches offer different advantages and disadvantages, लेकिन संरचना अक्सर होती है (आमतौर पर?) बेहतर *:

public class Protocol { 

     private ISpecialHandler specialHandler; 

     public Protocol() {} 

     public Protocol(ISpecialHandler specialHandler) { 
      this.specialHandler = specialHandler; 
     } 

     void Same1() {} 
     void Same2() {} 

     public void DoStuff() { 
      this.Same1(); 
      if (this.specialHandler != null) { 
       this.specialHandler.DoStuff(); 
      } 
      this.Same2(); 
     } 
} 

कॉलर्स तो वस्तु उदाहरणों में पारित कर सकते हैं (strategies) है कि विशेष एल्गोरिदम जो कुछ भी मामले को संभालने के लिए उपलब्ध कराने के हाथ में है:

Protocol protocol1 = new Protocol(new DiffStuffHandler()); 
protocol1.DoStuff(); 

* एक विस्तृत के लिए Patterns I Hate #2: Template Method देखें आपके मामले में विरासत की तुलना में संरचना आमतौर पर बेहतर क्यों होती है इसका स्पष्टीकरण।

+0

यह संभवतः उलटा संरचना की तुलना में प्रोटोकॉल के लिए बेहतर फिट है। –

+0

छोटे सुझाव जो मैं करूँगा, आईएसआईएल हैंडलर को इसे ठेकेदार में लेने के बजाय एक संपत्ति बनाना है। यह आपके एपीआई के कॉलर्स को विशेष रूप से पता है कि ISpecialHandler वैकल्पिक है। – BFree

0

आप पर विचार करने के पैटर्न के इस प्रकार से काम करता है कि क्या चाहते हो सकता है:

public class ProtocolHelper 
{ 
    public void Same1() {} 
    public void Same2() {} 
} 

public class Protocol1 : IProtocol 
{ 
    private readonly ProtocolHelper _helper = new ProtocolHelper(); 

    void MyInterfaceMethod1() 
    { 
     _helper.Same1(); 
     DiffStuff(); 
     _helper.Same2(); 
    } 
} 

आप बता सकते हैं कि अगर यह अगर तुम 'ProtocolHelper' वर्ग के लिए एक अच्छा नाम के साथ आने कर सकते हैं देख कर समझ में आता है। यदि आपका नाम स्वाभाविक रूप से आपके तर्क से उत्पन्न होता है, तो कक्षा को तोड़ने का यह एक अच्छा तरीका है। आपको इस कार्य को करने के लिए विधियों के पैरामीटर के रूप में कुछ निर्भरताओं (जैसे निजी फ़ील्ड) में पास करने की आवश्यकता हो सकती है।

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