这段代码中的设计模式是什么?

时间:2010-02-12 04:48:23

标签: c# design-patterns reflection factory

假设我有一个singleton-ish,factory-ish,reflection-ish类接收一些输入,并吐出一个接口的具体实现的新实例。这是什么样的设计?有没有更好的方法来做我想要的?

这里有一些代码来说明这一点:

using System;
using System.Collections.Generic;

// static factory class
public static class ArticleFactory 
{
    // given an SKU, store the Type object for an IArticle object
    private static Dictionary<string, Type> articleRegistry = new Dictionary<string, Type>();

    // allow public registration of SKU-to-Type object relationships
    public static bool Register(string sku, Type typeInfo)
    {
        if(!articleRegistry.ContainsKey(sku)) 
        {
            articleRegistry.Add(sku, typeInfo);
            return true;
        }
        return false;
    }

    // given a SKU, give me an instance of the related IArticle object
    public static IArticle NewArticle(string sku)
    {
        if(articleRegistry.ContainsKey(sku))
        {
            // use reflection to invoke the default constructor
            return articleRegistry[sku].GetConstructor(Types.EmptyTypes).Invoke(null) as IArticle;
        }
        return null;
    }
}

// example concrete-implementation of an IArticle
public class Jeans : IArticle 
{
    public decimal GetPrice() {  return SomeDecimal(); }
}

// WHERE DO I CALL THIS LINE? 
ArticleFactory.Register("0929-291", typeof(Jeans)); 

// Later on, if another group needs to write the class for Snowboards, 
// how can they self-register their class, without changing any "Main()"
// or "Page_Init()" function?

4 个答案:

答案 0 :(得分:2)

看起来你已经确定了这种模式。这是Factory Method Pattern。或者更确切地说,是一个有点半生不熟的实现。稍微好一点的方法是首先使它成为一个接口:

public interface IArticleFactory
{
    IArticle CreateArticle(string sku);
}

然后在没有任何反射的情况下实施工厂:

public class MyArticleFactory
{
    private Dictionary<string, Func<IArticle>> instantiators =
        new Dictionary<string, Func<Iarticle>>();

    public MyArticleFactory()
    {
        Register("Jeans", () => new Jeans());
        Register("Shirt", () => new Shirt());
        // etc.
    }

    public IArticle CreateArticle(string sku)
    {
        Func<IArticle> instantiator;
        if (creators.TryGetValue(sku, out instantiator))
            return instantiator();
        throw new UnknownSkuException(sku);
    }

    protected void Register(string sku, Func<IArticle> instantiator)
    {
        creators.Add(sku, instantiator);
    }
}

一些重要的差异:

  • 注册不公开,也不应该公开。注册通常要么位于某个配置文件中,要么是私有的。

  • 不要求IArticle具体类型具有默认的无参数构造函数。这可以很容易地使用参数化构造函数注册文章(只要知道要使用的参数)。

  • 在重复注册时引发异常。我不喜欢简单地回归false的想法;如果你尝试两次注册相同的工厂方法,那应该被认为是一个错误。

  • 这不是一成不变的。您可以使用其他工厂替换此工厂。你可以对它进行单元测试。

当然,更好的方法就是使用任何现有的.NET依赖注入/控制框架反转,例如NinjectAutoFac

答案 1 :(得分:2)

我不知道它是否有“名称”,但它看起来像某种手动服务解析器。我可以看到的问题(遗憾地来自经验)是它在实际上是不灵活的,因为:

  • 注册只有一个配置
  • 很难进行单元测试

如果我在新系统中这样做,我个人会看一个IoC容器; IoC可以处理这种关系,并提供更多免费功能(生命周期,充实,额外设置等),并解决许多相关问题。

顺便说一句,它可能更容易:

return Activator.CreateInstance(articleRegistry[sku]);

答案 2 :(得分:1)

我认为你在这里所做的基本上是依赖注入(或者控制反转就是很酷的孩子所说的)。看看这些链接:

维基百科的解释:http://en.wikipedia.org/wiki/Dependency_Injection

两个DI .Net框架:

StructureMap:http://structuremap.sourceforge.net/QuickStart.htm

Castle Windsor:http://www.castleproject.org/container/index.html

答案 3 :(得分:0)

这只是一个工厂模式,碰巧在其实现中使用反射。但是,不是使用反射,而是简单地将工厂类的实例直接放在字典中可能更有效,尽管这可能需要一些样板代码。