2008-10-09 10 views
8

मेरे कोड पर FxCop चल रहा है, मैं इस चेतावनी मिलती है:युग्मन बहुत अधिक है - इस वर्ग को बेहतर तरीके से कैसे डिजाइन करें?

Microsoft.Maintainability: 'FooBar.ctor 9 विभिन्न नामस्थान से 99 विभिन्न प्रकार के साथ मिलकर कर रहा है। अपनी कक्षा युग्मन, को कम करने के तरीके को रीवाइट या दोबारा दोहराएं या विधि को पर अन्य प्रकार के पर ले जाने पर विचार करें। 40 से ऊपर एक वर्ग युग्मन खराब रखरखाव इंगित करता है, 40 और 30 के बीच वर्ग युग्मन मध्यम रखरखाव, इंगित करता है और 30 से नीचे एक वर्ग युग्मन अच्छी रखरखाव इंगित करता है।

मेरी कक्षा सर्वर से सभी संदेशों के लिए एक लैंडिंग क्षेत्र है। सर्वर हमें अलग EventArgs प्रकार के संदेश भेज सकते हैं:

public FooBar() 
{ 
    var messageHandlers = new Dictionary<Type, Action<EventArgs>>(); 
    messageHandlers.Add(typeof(YouHaveBeenLoggedOutEventArgs), HandleSignOut); 
    messageHandlers.Add(typeof(TestConnectionEventArgs), HandleConnectionTest); 
    // ... etc for 90 other types 
} 

"HandleSignOut" और "HandleConnectionTest" तरीकों उन में छोटे से कोड है: वे आम तौर पर किसी अन्य वर्ग में एक समारोह में काम को पास करते हैं।

मैं इस कक्षा को कम युग्मन के साथ कैसे बेहतर बना सकता हूं?

+0

मुझे यहां प्रतिक्रियाएं पसंद हैं लेकिन शायद कुछ जगह जैसे RefactorMyCode.com इसके लिए एक बेहतर जगह हो सकती है। हर किसी के लिए उस साइट पर उनके जवाब में कोड पोस्ट करना थोड़ा आसान है। –

+0

ठीक है, मैंने उदाहरण पोस्ट किया है, इसे उत्तर दें –

उत्तर

15

कक्षाएं ऐसा है उन कार्यक्रमों के लिए कार्य रजिस्टर करें जिनमें वे रुचि रखते हैं ... event broker पैटर्न।

class EventBroker { 
    private Dictionary<Type, Action<EventArgs>> messageHandlers; 

    void Register<T>(Action<EventArgs> subscriber) where T:EventArgs { 
     // may have to combine delegates if more than 1 listener 
     messageHandlers[typeof(T)] = subscriber; 
    } 

    void Send<T>(T e) where T:EventArgs { 
     var d = messageHandlers[typeof(T)]; 
     if (d != null) { 
     d(e); 
     } 
    } 
} 
+0

बिल्कुल वही जो मैं बस बात कर रहा था। मैं उदाहरण के कारण आपका वोट दे रहा हूं। – NotMe

+0

दिलचस्प! उत्तर के लिए धन्यवाद, हम इसे ध्यान में रखेंगे। मुझे लगता है कि जब तक मैं एक बेहतर नहीं देखता, मैं जवाब के रूप में आपका स्वीकार करूंगा। –

+0

दिलचस्प! मैं एक ही मुद्दे में भाग गया। क्या यह पर्याप्त तेज़ है? मेरा मतलब है, क्या मैं इसे एक अद्यतन लूप के साथ एक गेम में उपयोग कर सकता हूं? – Vinz243

0

शायद प्रत्येक संदेश के लिए एक अलग वर्ग रखने के बजाय, संदेश की पहचान करने वाले ध्वज का उपयोग करें।

इससे आपके संदेशों की संख्या में कमी आएगी और रखरखाव में वृद्धि होगी। मेरा अनुमान है कि अधिकांश संदेश वर्गों में शून्य अंतर होता है।

इस पर हमला करने का एक अतिरिक्त तरीका चुनना मुश्किल है क्योंकि बाकी वास्तुकला अज्ञात है (मेरे लिए)।

यदि आप विंडोज देखते हैं, उदाहरण के लिए, यह मूल रूप से नहीं जानता है कि प्रत्येक संदेश को कैसे संभाला जा सकता है। इसके बजाए, अंतर्निहित संदेश हैंडलर मुख्य थ्रेड के साथ कॉलबैक फ़ंक्शन पंजीकृत करते हैं।

आप एक समान दृष्टिकोण ले सकते हैं। प्रत्येक संदेश वर्ग को यह जानना होगा कि खुद को कैसे संभालना है और बड़े आवेदन के साथ खुद को पंजीकृत कर सकता है। यह कोड को बहुत सरल बनाना चाहिए और तंग युग्मन से छुटकारा पाएं।

+0

उत्तर के लिए धन्यवाद। हमने कुछ समय पहले यह कोशिश की, लेकिन यह जटिलता को हैंडलर फ़ंक्शंस में बदल देता है - 99 छोटे हैंडलर फ़ंक्शंस के बजाय, आपके पास 20 बहुत बड़े हैंडलर फ़ंक्शंस हैं। कोई बेहतर विचार? –

5

आप शब्दकोश को इंजेक्ट करने के लिए Spring.NET जैसे कुछ प्रकार के आईओसी फ्रेमवर्क का भी उपयोग कर सकते हैं। इस तरह, यदि आपको कोई नया संदेश प्रकार मिलता है, तो आपको इस केंद्रीय केंद्र को फिर से कंपाइल करने की आवश्यकता नहीं है - बस एक कॉन्फ़िगरेशन फ़ाइल बदलें।


लंबे समय से प्रतीक्षा उदाहरण:

एक नया कंसोल अनुप्रयोग, उदाहरण नामित बनाएँ, और जोड़ने के इस:

using System; 
using System.Collections.Generic; 
using Spring.Context.Support; 

namespace Example 
{ 
    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      MessageBroker broker = (MessageBroker) ContextRegistry.GetContext()["messageBroker"]; 
      broker.Dispatch(null, new Type1EventArgs()); 
      broker.Dispatch(null, new Type2EventArgs()); 
      broker.Dispatch(null, new EventArgs()); 
     } 
    } 

    public class MessageBroker 
    { 
     private Dictionary<Type, object> handlers; 

     public Dictionary<Type, object> Handlers 
     { 
      get { return handlers; } 
      set { handlers = value; } 
     } 

     public void Dispatch<T>(object sender, T e) where T : EventArgs 
     { 
      object entry; 
      if (Handlers.TryGetValue(e.GetType(), out entry)) 
      { 
       MessageHandler<T> handler = entry as MessageHandler<T>; 
       if (handler != null) 
       { 
        handler.HandleMessage(sender, e); 
       } 
       else 
       { 
        //I'd log an error here 
        Console.WriteLine("The handler defined for event type '" + e.GetType().Name + "' doesn't implement the correct interface!"); 
       } 
      } 
      else 
      { 
       //I'd log a warning here 
       Console.WriteLine("No handler defined for event type: " + e.GetType().Name); 
      } 
     } 
    } 

    public interface MessageHandler<T> where T : EventArgs 
    { 
     void HandleMessage(object sender, T message); 
    } 

    public class Type1MessageHandler : MessageHandler<Type1EventArgs> 
    { 
     public void HandleMessage(object sender, Type1EventArgs args) 
     { 
      Console.WriteLine("Type 1, " + args.ToString()); 
     } 
    } 

    public class Type2MessageHandler : MessageHandler<Type2EventArgs> 
    { 
     public void HandleMessage(object sender, Type2EventArgs args) 
     { 
      Console.WriteLine("Type 2, " + args.ToString()); 
     } 
    } 

    public class Type1EventArgs : EventArgs {} 

    public class Type2EventArgs : EventArgs {} 
} 

और एक app.config फ़ाइल:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
    <configSections> 
    <sectionGroup name="spring"> 
     <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/> 
     <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/> 
    </sectionGroup> 
    </configSections> 

    <spring> 
    <context> 
     <resource uri="config://spring/objects"/> 
    </context> 
    <objects xmlns="http://www.springframework.net"> 

     <object id="messageBroker" type="Example.MessageBroker, Example"> 
     <property name="handlers"> 
      <dictionary key-type="System.Type" value-type="object"> 
      <entry key="Example.Type1EventArgs, Example" value-ref="type1Handler"/> 
      <entry key="Example.Type2EventArgs, Example" value-ref="type2Handler"/> 
      </dictionary> 
     </property> 
     </object> 
     <object id="type1Handler" type="Example.Type1MessageHandler, Example"/> 
     <object id="type2Handler" type="Example.Type2MessageHandler, Example"/> 
    </objects> 
    </spring> 
</configuration> 

आउटपुट:

 
Type 1, Example.Type1EventArgs 
Type 2, Example.Type2EventArgs 
No handler defined for event type: EventArgs 

जैसा कि आप देख सकते हैं, MessageBroker किसी भी हैंडलर के बारे में नहीं जानता है, और हैंडलर को MessageBroker के बारे में पता नहीं है। ऐप में सभी मानचित्रण किया जाता है।कॉन्फ़िगरेशन फ़ाइल, ताकि यदि आपको कोई नया ईवेंट प्रकार संभालने की आवश्यकता है, तो आप इसे कॉन्फ़िगरेशन फ़ाइल में जोड़ सकते हैं। यह विशेष रूप से अच्छा है अगर अन्य टीम घटना प्रकारों और हैंडलर को परिभाषित कर रही हैं - वे सिर्फ अपनी सामग्री को एक डीएल में संकलित कर सकते हैं, आप इसे अपनी तैनाती में छोड़ दें और बस मैपिंग जोड़ें।

डिक्शनरी में MessageHandler<> की बजाय टाइप ऑब्जेक्ट के मान हैं क्योंकि वास्तविक हैंडलर को MessageHandler<EventArgs> पर नहीं डाला जा सकता है, इसलिए मुझे थोड़ा सा हैक करना पड़ा। मुझे लगता है कि समाधान अभी भी साफ है, और यह मैपिंग त्रुटियों को अच्छी तरह से संभालता है। ध्यान दें कि आपको इस प्रोजेक्ट में Spring.Core.dll को संदर्भित करने की भी आवश्यकता होगी। आप पुस्तकालय here, और प्रलेखन here पा सकते हैं। इसके लिए प्रासंगिक है। यह भी ध्यान रखें, इसके लिए Spring.NET का उपयोग करने की कोई आवश्यकता नहीं है - यहां महत्वपूर्ण विचार निर्भरता इंजेक्शन है। किसी भी तरह, ब्रोकर को एक्स के प्रकार के संदेश भेजने के लिए कुछ कहने की आवश्यकता होगी, और निर्भरता इंजेक्शन के लिए आईओसी कंटेनर का उपयोग करना ब्रोकर को एक्स के बारे में नहीं पता, और इसके विपरीत है।

कुछ अन्य अतः आईओसी और डि से संबंधित सवाल:

+0

उत्तर के लिए धन्यवाद। अच्छा, यह दिलचस्प लगता है।मैं एक पहलू पर अस्पष्ट हूं: क्या आईओसी ढांचा अभी भी मेरे हैंडलर कार्यों का उपयोग कर सकता है? मेरा मतलब है, मेरा शब्दकोश EventArg कुंजी और हैंडलर फ़ंक्शन मान शामिल है। Ioc अभी भी हैंडलर फ़ंक्शन मानों के साथ काम करता है? –

+0

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

+0

ठीक है, इसलिए 99 प्रकारों के बारे में जानने के लिए फूबर के बजाय, हमारे पास 99 प्रकार के बारे में जानने के लिए FooBar के बारे में जानकारी होगी। मुझे एक नमूना देखना अच्छा लगेगा। –

0

मैं अपने कोड के बाकी नहीं दिख रहा है, लेकिन मैं कोशिश करेगा इवेंट Arg कक्षाओं की एक बहुत छोटी संख्या बनाएँ। इसके बजाय कुछ ऐसे डेटा बनाएं जो डेटा के निहित डेटा के संदर्भ में एक दूसरे के समान हों और/या जिस तरह से आप उन्हें बाद में संभालेंगे और एक फ़ील्ड जोड़ें जो आपको बताएगा कि सटीक प्रकार की घटना कब हुई (शायद आपको एक enum का उपयोग करना चाहिए)।

आदर्श रूप में आप केवल इस निर्माता और अधिक पठनीय नहीं होगा, लेकिन यह भी जिस तरह से संदेश (समूह संदेश है कि एक ही ईवेंट हैंडलर में एक समान तरीके से नियंत्रित किया जाता है) से नियंत्रित किया जाता

+0

उत्तर के लिए धन्यवाद। क्रिस लाइवली ने घटना तर्क कक्षाओं की छोटी संख्या का सुझाव दिया। हमने कोशिश की, और यह जटिलता को बदल देता है: 99 छोटे हैंडलर कार्यों के बजाय, अब हमारे पास 20 बड़े हैंडलर फ़ंक्शन हैं। –

+0

"समूह संदेश जो एक ही ईवेंट हैंडलर में समान तरीके से संभाले जाते हैं" के संबंध में, हां, हम ऐसा करते हैं। हमारे पास लगभग 20 घटना तर्क हैं जो एक ही तरीके से संभाले जाते हैं और एक ही हैंडलर को पास कर देते हैं। –

0

जाहिर है आप एक भेजने तंत्र की जरूरत होती हैं: घटना आपको प्राप्त के आधार पर, आप अलग अलग कोड निष्पादित करने के लिए चाहते हैं।

आप घटनाओं की पहचान करने के लिए टाइप सिस्टम का उपयोग कर रहे हैं, जबकि वास्तव में यह बहुरूपता का समर्थन करने के लिए है। जैसा कि क्रिस लाइवली सुझाव देता है, आप बस भी (सिस्टम सिस्टम का दुरुपयोग किए बिना) संदेशों की पहचान करने के लिए एक गणना का उपयोग कर सकते हैं।

या आप टाइप सिस्टम की शक्ति को गले लगा सकते हैं, और एक रजिस्ट्री ऑब्जेक्ट बना सकते हैं जहां हर प्रकार की घटना पंजीकृत होती है (एक स्थिर उदाहरण, एक कॉन्फ़िगरेशन फ़ाइल या जो भी हो)। फिर आप उचित हैंडलर खोजने के लिए जिम्मेदारी पैटर्न की श्रृंखला का उपयोग कर सकते हैं। या तो हैंडलर खुद को संभालने में काम करता है, या यह फैक्ट्री हो सकता है, जो एक ऑब्जेक्ट बनाता है जो घटना को संभालता है।

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

+0

मुझे आपके सुझाव को समझ में नहीं आता है। क्या आप मुझे अधिक संसाधनों के लिए इंगित कर सकते हैं या एक उदाहरण पोस्ट कर सकते हैं? –

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