سؤال

أنا أعمل على مشروع داخلي لشركتي، وجزء من المشروع هو أن أتمكن من تحليل "المهام" المتنوعة من ملف XML إلى مجموعة من المهام ليتم تشغيلها لاحقًا.

نظرًا لأن كل نوع من المهام يحتوي على العديد من المجالات المرتبطة المختلفة، فقد قررت أنه سيكون من الأفضل تمثيل كل نوع من المهام في فئة منفصلة.

للقيام بذلك، قمت ببناء فئة أساسية مجردة:

public abstract class Task
{
    public enum TaskType
    {
        // Types of Tasks
    }   

    public abstract TaskType Type
    {
        get;
    }   

    public abstract LoadFromXml(XmlElement task);
    public abstract XmlElement CreateXml(XmlDocument currentDoc);
}

كل مهمة موروثة من هذه الفئة الأساسية، وتضمنت التعليمات البرمجية اللازمة لإنشاء نفسها من تمريرها في XmlElement، بالإضافة إلى إجراء تسلسل لنفسها مرة أخرى إلى XmlElement.

مثال أساسي:

public class MergeTask : Task
{

    public override TaskType Type
    {
        get { return TaskType.Merge; }
    }   

    // Lots of Properties / Methods for this Task

    public MergeTask (XmlElement elem)
    {
        this.LoadFromXml(elem);
    }

    public override LoadFromXml(XmlElement task)
    {
        // Populates this Task from the Xml.
    }

    public override XmlElement CreateXml(XmlDocument currentDoc)
    {
        // Serializes this class back to xml.
    }
}

سيستخدم المحلل اللغوي بعد ذلك رمزًا مشابهًا لهذا لإنشاء مجموعة مهام:

XmlNode taskNode = parent.SelectNode("tasks");

TaskFactory tf = new TaskFactory();

foreach (XmlNode task in taskNode.ChildNodes)
{
    // Since XmlComments etc will show up
    if (task is XmlElement)
    {
        tasks.Add(tf.CreateTask(task as XmlElement));
    }
}

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

ومع ذلك، فأنا لست سعيدًا بالرمز الخاص بي الخاص بـ TaskFactory.CreateTask.تقبل هذه الطريقة XmlElement، ثم تقوم بإرجاع مثيل لفئة المهمة المناسبة:

public Task CreateTask(XmlElement elem)
{
    if (elem != null)
    {
        switch(elem.Name)
        {
            case "merge":
                return new MergeTask(elem);
            default:
                throw new ArgumentException("Invalid Task");
        }
    }
}

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

اي نصيحه؟

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

المحلول

أستخدم الانعكاس للقيام بذلك.يمكنك إنشاء مصنع يتوسع بشكل أساسي دون الحاجة إلى إضافة أي كود إضافي.

تأكد من "استخدام System.Reflection"، ضع الكود التالي في طريقة إنشاء مثيلك.

public Task CreateTask(XmlElement elem)
{
    if (elem != null)
    { 
        try
        {
          Assembly a = typeof(Task).Assembly
          string type = string.Format("{0}.{1}Task",typeof(Task).Namespace,elem.Name);

          //this is only here, so that if that type doesn't exist, this method
          //throws an exception
          Type t = a.GetType(type, true, true);

          return a.CreateInstance(type, true) as Task;
        }
        catch(System.Exception)
        {
          throw new ArgumentException("Invalid Task");
        }
    }
}

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

نصائح أخرى

قم بإنشاء مثيل "نموذج أولي" لكل فئة ووضعها في جدول تجزئة داخل المصنع، مع استخدام السلسلة التي تتوقعها في ملف XML كمفتاح.

لذا فإن CreateTask يجد فقط كائن النموذج الأولي الصحيح ، عن طريق الحصول على () من علامة التجزئة.

ثم اتصل بـ LoadFromXML عليه.

يجب عليك تحميل الفئات مسبقًا في جدول التجزئة،

إذا كنت تريد المزيد من التلقائية ...

يمكنك جعل الفئات "مسجَّلة ذاتيًا" عن طريق استدعاء طريقة تسجيل ثابتة في المصنع.

قم بإجراء مكالمات للتسجيل (مع المُنشئين) في الكتل الثابتة في فئات المهام الفرعية.ثم كل ما عليك فعله هو "ذكر" الفئات لتشغيل الكتل الثابتة.

ستكون مجموعة ثابتة من الفئات الفرعية للمهام كافية بعد ذلك "لذكرها".أو استخدم الانعكاس لذكر الفصول.

ما هو شعورك تجاه حقن التبعية؟أستخدم Ninject وسيكون دعم الربط السياقي فيه مثاليًا لهذه الحالة.أنظر إلى هذا مشاركة مدونة حول كيفية استخدام الربط السياقي من خلال إنشاء وحدات تحكم باستخدام IControllerFactory عند طلبها.يجب أن يكون هذا مصدرًا جيدًا لكيفية استخدامه لموقفك.

@jholland

لا أعتقد أن هناك حاجة إلى نوع التعداد، لأنه يمكنني دائمًا القيام بشيء مثل هذا:

التعداد؟

أعترف أنه يشعر بالاختراق.قد يبدو التفكير قذرًا في البداية، ولكن بمجرد ترويض الوحش ستستمتع بما يتيح لك القيام به.(تذكر العودية، فهي تبدو قذرة، ولكنها جيدة)

الحيلة هي أن تدرك أنك تقوم بتحليل بيانات التعريف، وهي في هذه الحالة سلسلة مقدمة من ملف XML، وتحولها إلى سلوك وقت التشغيل.هذا هو أفضل ما في الانعكاس.

بالمناسبة:عامل التشغيل هو انعكاس أيضًا.

http://en.wikipedia.org/wiki/Reflection_(computer_science)#الاستخدامات

@Tim، انتهى بي الأمر باستخدام نسخة مبسطة من أسلوبك وChansChans، إليك الكود:

public class TaskFactory
    {
        private Dictionary<String, Type> _taskTypes = new Dictionary<String, Type>();

        public TaskFactory()
        {
            // Preload the Task Types into a dictionary so we can look them up later
            foreach (Type type in typeof(TaskFactory).Assembly.GetTypes())
            {
                if (type.IsSubclassOf(typeof(CCTask)))
                {
                    _taskTypes[type.Name.ToLower()] = type;
                }
            }
        }

        public CCTask CreateTask(XmlElement task)
        {
            if (task != null)
            {
                string taskName = task.Name;
                taskName =  taskName.ToLower() + "task";

                // If the Type information is in our Dictionary, instantiate a new instance of that task
                Type taskType;
                if (_taskTypes.TryGetValue(taskName, out taskType))
                {
                    return (CCTask)Activator.CreateInstance(taskType, task);
                }
                else
                {
                    throw new ArgumentException("Unrecognized Task:" + task.Name);
                }                               
            }
            else
            {
                return null;
            }
        }
    }

@تشان تشان

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

لقد جعلتني أفكر، لا أعتقد أن هناك حاجة إلى نوع التعداد، لأنه يمكنني دائمًا القيام بشيء مثل هذا:

if (CurrentTask is MergeTask)
{
    // Do Something Specific to MergeTask
}

ربما ينبغي لي أن أفتح كتاب GoF Design Patterns الخاص بي مرة أخرى، لكنني اعتقدت حقًا أن هناك طريقة لإنشاء مثيل للفصل الصحيح بشكل متعدد الأشكال.

التعداد؟

كنت أشير إلى خاصية النوع والتعداد في صفي التجريدي.

انعكاس ذلك الحين!سأضع علامة على إجابتك على أنها مقبولة في حوالي 30 دقيقة، فقط لإعطاء الوقت لأي شخص آخر لتقييمه.إنه موضوع ممتع

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

@ ديل

لم أقم بفحص nInject عن كثب، ولكن من خلال فهمي العالي لحقن التبعية، أعتقد أنه سيحقق نفس الشيء مثل اقتراح ChanChans، فقط مع المزيد من الطبقات (أي التجريد).

في موقف لمرة واحدة حيث أحتاج إليه هنا فقط، أعتقد أن استخدام بعض رموز الانعكاس التي يتم تمريرها يدويًا هو أسلوب أفضل من وجود مكتبة إضافية للارتباط بها واستدعاءها في مكان واحد فقط ...

لكن ربما لا أفهم الميزة التي قد يمنحها لي nInject هنا.

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

مع Ninject، الشيء الرئيسي الذي أعجبني فيه، هو أنه عندما يحتاج إلى استخدام الانعكاس، فإنه لا يفعل ذلك.وبدلاً من ذلك فإنه يستفيد من ميزات إنشاء التعليمات البرمجية الخاصة بـ .NET والتي تجعله سريعًا بشكل لا يصدق.إذا كنت تشعر أن التفكير سيكون أسرع في السياق الذي تستخدمه، فإنه يسمح لك أيضًا بإعداده بهذه الطريقة.

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

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