Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 抽象工厂设计模式_C#_Design Patterns_Factory - Fatal编程技术网

C# 抽象工厂设计模式

C# 抽象工厂设计模式,c#,design-patterns,factory,C#,Design Patterns,Factory,我正在为我的公司进行一个内部项目,该项目的一部分是能够将XML文件中的各种“任务”解析为稍后运行的任务集合 因为每种类型的任务都有许多不同的相关字段,所以我决定最好用一个单独的类来表示每种类型的任务 为此,我构建了一个抽象基类: public abstract class Task { public enum TaskType { // Types of Tasks } public abstract TaskType Type {

我正在为我的公司进行一个内部项目,该项目的一部分是能够将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个案例)开关来选择要实例化的子类。我希望这里能有一些多态技巧来清理这个方法


有什么建议吗?

我用反射来做这件事。 您可以创建一个基本上可以扩展的工厂,而无需添加任何额外代码

确保您有“usingsystem.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");
        }
    }
}
另一个观察结果是,您可以将此方法设置为静态,并将其挂起到Task类中,这样您就不必重新创建TaskFactory,还可以为自己保存一个要维护的移动部件。

@ChanChan

我喜欢反射的想法,但同时我总是羞于使用反射。我总是觉得围绕一些本应该更容易的事情工作是一种“黑客”。我确实考虑过这种方法,然后算出一个开关语句,对于相同数量的代码气味来说,更快。 你确实让我想到了,我认为不需要类型enum,因为我总是可以这样做:

if (CurrentTask is MergeTask)
{
    // Do Something Specific to MergeTask
}
也许我应该再次打开我的GoF设计模式书,但我真的认为有一种方法可以多态地实例化正确的类。

@jholland

我认为不需要类型enum,因为我总是可以这样做:

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

我承认这感觉很不舒服。反射一开始感觉很肮脏,但一旦你驯服了野兽,你就会享受它让你做的事情。(记住递归,它感觉脏,但它很好)

诀窍是要认识到,您正在分析元数据,在本例中是从xml提供的字符串,并将其转换为运行时行为。这就是反思最擅长的

顺便说一句:这是运算符,也是反射。

枚举

我在抽象类中引用了类型属性和枚举


那就是了!我会在大约30分钟内将你们的答案标记为已被接受,以便给其他人时间来衡量。这是一个有趣的话题。

谢谢你让它打开,我不会抱怨的。这是一个有趣的话题,我希望你能多态性地实例化。

甚至ruby(及其高级元编程)也必须使用其反射机制来实现这一点。

您对依赖注入有何看法?我使用Ninject,其中的上下文绑定支持非常适合这种情况。了解如何使用上下文绑定在请求时使用IControllerFactory创建控制器。这应该是一个很好的资源,告诉你如何在你的情况下使用它。

@Dale

我没有仔细检查过nInject,但从我对依赖注入的高级理解来看,我相信它将完成与Chanchan建议相同的事情,只是需要更多层次的粗糙(er抽象)

在一次过的情况下,我只需要在这里使用它,我认为使用一些手工编写的反射代码是一种更好的方法,而不是有一个额外的库来链接,只调用一个地方


但是也许我不明白nInject在这里给我带来的好处。

有些框架可能在需要的地方依赖反射,但是大多数时候,如果你愿意,你会使用引导程序来设置在需要对象实例时该做什么。这通常存储在通用词典中。直到最近,我才开始使用Ninject

对于Ninject,我最喜欢的是,当它确实需要使用反射时,它就不需要了。相反,它利用了.NET的代码生成功能,这使得它的速度非常快。如果您觉得反射在您使用的上下文中会更快,那么它也允许您以这种方式进行设置

我知道这可能是你目前所需要的,但我只是想指出依赖注入,给你一些思考未来的食物。请访问获取课程。

为每个类创建一个“原型”实例,并将它们放在工厂内的哈希表中,以XML中预期的字符串作为键

所以CreateTask只是找到了正确的原型对象, 通过从哈希表中获取()

然后对其调用LoadFromXML

必须将类预加载到哈希表中

如果你想让它更自动化

通过在工厂中调用静态寄存器方法,可以使类“自注册”

在任务子类的静态块中放置要注册(使用构造函数)的调用。 然后,您所需要做的就是“提及”类以运行静态块

任务子类的静态数组就足以“提及”它们。
或者使用反射来提及这些类。

@Tim,我最终使用了您的方法和通道的简化版本,下面是代码:

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;
            }
        }
    }
公共类任务工厂
{
专用字典\u任务类型=