الاقتران مرتفع جدًا - كيف يتم تصميم هذه الفئة بشكل أفضل؟

StackOverflow https://stackoverflow.com/questions/189156

  •  06-07-2019
  •  | 
  •  

سؤال

عند تشغيل FxCop على الكود الخاص بي، أحصل على هذا التحذير:

مايكروسوفت. قابلية الصيانة:يقترن Foobar.ctor بـ 99 نوعًا مختلفًا من 9 مساحات أسماء مختلفة.أعد كتابة أو إعادة صياغة طريقة تقليل اقتران الفئة ، أو التفكير في نقل الطريقة إلى أحد الأنواع الأخرى التي يقترن بها بإحكام.يشير الاقتران الطبقي فوق 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" على تعليمات برمجية قليلة؛عادةً ما يقومون بتمرير العمل إلى وظيفة في فصل آخر.

كيف يمكنني تحسين هذه الفئة باستخدام أداة التوصيل المنخفضة؟

هل كانت مفيدة؟

المحلول

هل لديك الفئات التي تفعل سجل عمل للأحداث أنهم مهتمون ... و<لأ href = "http://msforge.net/blogs/paki/archive/2007/11/20/EventBroker-implementation -في-C_2300_-كامل مصدر code.aspx "يختلط =" نوفولو noreferrer "> وسيط الحدث النمط.

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);
      }
   }
}

نصائح أخرى

يمكنك أيضًا استخدام أحد أنواع إطار عمل IoC، مثل 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.يتم إجراء كل التعيينات في ملف app.config، بحيث إذا كنت بحاجة إلى التعامل مع نوع حدث جديد، يمكنك إضافته في ملف التكوين.يعد هذا أمرًا رائعًا بشكل خاص إذا كانت الفرق الأخرى تحدد أنواع الأحداث ومعالجاتها - يمكنهم فقط تجميع عناصرهم في ملف dll، وإسقاطها في عملية النشر الخاصة بك وإضافة التعيين ببساطة.

يحتوي القاموس على قيم كائن الكتابة بدلاً من MessageHandler<> لأنه لا يمكن إرسال المعالجات الفعلية إليها MessageHandler<EventArgs>, ، لذلك اضطررت إلى الالتفاف حول ذلك قليلاً.أعتقد أن الحل لا يزال نظيفًا، ويتعامل مع أخطاء التعيين بشكل جيد.لاحظ أنك ستحتاج أيضًا إلى الرجوع إلى Spring.Core.dll في هذا المشروع.يمكنك العثور على المكتبات هنا, ، والتوثيق هنا.ال فصل حقن التبعية ذات صلة بهذا.لاحظ أيضًا أنه لا يوجد سبب يدفعك لاستخدام Spring.NET لهذا الغرض - الفكرة المهمة هنا هي حقن التبعية.بطريقة ما، سيلزم شيء ما لإخبار الوسيط بإرسال رسائل من النوع a إلى x، ويعد استخدام حاوية IoC لحقن التبعية طريقة جيدة لجعل الوسيط لا يعرف شيئًا عن x، والعكس صحيح.

بعض أسئلة SO الأخرى المتعلقة بـ IoC و DI:

وأنا لا أرى ما تبقى من التعليمات البرمجية الخاصة بك، ولكن أود أن محاولة إنشاء عدد أصغر بكثير من الطبقات الحدث وسيطة. بدلا من ذلك إنشاء القليلة التي تشبه بعضها البعض من حيث البيانات الواردة و / أو الطريقة التي تعامل معها في وقت لاحق وإضافة الحقل الذي سوف اقول لكم ما هو نوع الدقيق للحدث حدث (ربما يجب عليك استخدام التعداد).

وبشكل مثالي أنك لن يؤدي إلا إلى هذا المنشئ بكثير أكثر قابلية للقراءة، ولكن أيضا الطريقة التي يتم بها التعامل مع الرسائل (مجموعة رسائل التي يتم معالجتها بطريقة مشابهة في معالج حدث واحد)

وربما بدلا من وجود فئة مختلفة لكل رسالة، واستخدام العلم الذي يحدد الرسالة.

وهذا من شأنه أن يقلل بشكل كبير من عدد من الرسائل لديك وزيادة الصيانة. تخميني هو أن معظم فئات الرسالة دينا حوالي فرق صفر.

ومن الصعب اختيار وسيلة إضافية لمهاجمة هذا لأن بقية العمارة هي غير معروفة (بالنسبة لي).

إذا نظرتم الى ويندوز، على سبيل المثال، فإنه لا يعرف أصلا كيفية التعامل مع كل رسالة التي قد القيت عنه. بدلا من ذلك، معالجات الرسالة الأساسية تسجل ظائف رد مع موضوع الرئيسي.

وقد تتخذ نهجا مماثلة و. فإن كل فئة الرسالة في حاجة الى معرفة كيفية التعامل مع نفسها، ويمكن تسجيل نفسه مع تطبيق أكبر. وهذا ينبغي تبسيط كبير في رمز والتخلص من اقتران مشددة.

من الواضح أنك في حاجة إلى آلية إيفاد: اعتمادا على الحدث التي تتلقاها، تريد تنفيذ التعليمات البرمجية مختلفة

.

ويبدو أنك تستخدم نظام نوع للتعرف على الأحداث، في حين أنه من المفترض في الواقع لدعم تعدد الأشكال. كما يقترح كريس حية، هل يمكن فقط كذلك (دون إساءة استخدام النظام نوع) استخدام اعدد لتحديد الرسائل.

وأو يمكنك احتضان قوة النظام نوع، وإنشاء كائن التسجيل حيث يتم تسجيل كل نوع من الحدث (عن طريق مثيل ثابت، ملف التكوين أو على الإطلاق). ثم هل يمكن استخدام سلسلة من نمط المسؤولية للعثور على معالج الصحيح. إما معالج يفعل التعامل مع نفسها، أو أنها قد تكون مصنع، وخلق كائن الذي يعالج الحدث.

والأسلوب الأخير يبدو underspecified قليلا وoverengineered، ولكن في حالة من 99 أنواع الحدث (بالفعل)، فإنه يبدو من المناسب بالنسبة لي.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top