【问题标题】:Replace factory with AutoFac用 AutoFac 替换工厂
【发布时间】:2013-08-21 20:11:48
【问题描述】:

我习惯于创建自己的工厂,如图所示(为了说明而简化了):

public class ElementFactory
{
    public IElement Create(IHtml dom)
    {
        switch (dom.ElementType)
        {
            case "table":
                return new TableElement(dom);
            case "div":
                return new DivElement(dom);
            case "span":
                return new SpanElement(dom);
        }
        return new PassthroughElement(dom);
    }
}

我终于开始在我当前的项目中使用 IoC 容器 (AutoFac),我想知道是否有一些神奇的方法可以用 AutoFac 优雅地实现同样的事情?

【问题讨论】:

  • 用Autofac代替这个工厂有什么好处?
  • 抱歉,正如我在对 KeithS 的回答的评论中提到的那样,我希望有一些 AutoFac 巫术可以让我避免切换。避免切换有什么好处?我不知道......我似乎只是被灌输了对他们的厌恶:(
  • IoC 不是魔杖。您可以使用命名注册,也可以移除开关,但最终总会有某种开关逻辑。我的建议:使用工厂堡垒并在 Autofac 中注册工厂。

标签: c# autofac factory-pattern


【解决方案1】:

简短回答:是的。

更长的答案:首先,在类 Foo 被注册为 IFoo 的实现的简单情况下,Func<IFoo> 类型的构造函数参数或属性将由 Autofac 自动解析,无需额外接线。 Autofac 将注入一个在调用时基本上执行container.Resolve<IFoo>() 的委托。

在像你这样更复杂的情况下,返回的确切具体是基于输入参数,你可以做两件事之一。首先,您可以注册一个工厂方法作为其返回值,以提供参数化解析:

builder.Register<IElement>((c, p) => {
    var dom= p.Named<IHtml>("dom");
    switch (dom.ElementType)
    {
        case "table":
            return new TableElement(dom);
        case "div":
            return new DivElement(dom);
        case "span":
            return new SpanElement(dom);
    }
    return new PassthroughElement(dom);
  });

//usage
container.Resolve<IElement>(new NamedParameter("dom", domInstance))

这不是类型安全的(domInstance 不会经过编译器检查以确保它是 IHtml),也不是很干净。相反,另一种解决方案是将工厂方法实际注册为 Func:

builder.Register<Func<IHtml, IElement>>(dom =>
{
    switch (dom.ElementType)
    {
        case "table":
            return new TableElement(dom);
        case "div":
            return new DivElement(dom);
        case "span":
            return new SpanElement(dom);
    }
    return new PassthroughElement(dom);
});


public class NeedsAnElementFactory //also registered in AutoFac
{
    protected Func<IHtml,IElement> CreateElement {get; private set;}

    //AutoFac will constructor-inject the Func you registered
    //whenever this class is resolved.
    public NeedsAnElementFactory(Func<IHtml,IElement> elementFactory)
    {
        CreateElement = elementFactory;
    }  

    public void MethodUsingElementFactory()
    {
        IHtml domInstance = GetTheDOM();

        var element = CreateElement(domInstance);

        //the next line won't compile;
        //the factory method is strongly typed to IHtml
        var element2 = CreateElement("foo");
    }
}

如果您想将代码保留在 ElementFactory 中而不是将其放入 Autofac 模块中,您可以将工厂方法设为静态并注册它(这在您的情况下特别有效,因为您的工厂方法很容易被设为静态):

public class ElementFactory
{
    public static IElement Create(IHtml dom)
    {
        switch (dom.ElementType)
        {
            case "table":
                return new TableElement(dom);
            case "div":
                return new DivElement(dom);
            case "span":
                return new SpanElement(dom);
        }
        return new PassthroughElement(dom);
    }
}

...

builder.Register<Func<IHtml, IElement>>(ElementFactory.Create);

【讨论】:

  • 谢谢,这真的很有用!我有点希望 AutoFac 能帮助我避免丑陋的 switch 语句,但它就在那里,盯着我看,实际上是在嘲笑我,在你的所有三个代码块中:)
  • 好吧,也可以使用“键控注册”,将每个元素类型名称指定为相应对象类型的解析方法的键。这种情况的复杂性在于,并非所有可能的返回类型都与键相关联(您有一个默认情况),并且出于其他原因需要元素名称的 IHtml 父级。因此,最简单的做法是获取您的工作代码并进行设置,以便 AutoFac 可以使用它。
  • 注册无法编译:builder.Register&lt;Func&lt;IHtml, IElement&gt;&gt;(ElementFactory.Create); 我希望它看起来像这样:builder.Register&lt;Func&lt;IHtml, IElement&gt;&gt;((context, parameters) =&gt; (html =&gt; ElementFactory.Create(html)));
  • 或者更简单:context => builder.Register&lt;Func&lt;IHtml, IElement&gt;&gt;(context =&gt; ElementFactory.Create);
猜你喜欢
  • 2023-03-14
  • 2016-12-12
  • 1970-01-01
  • 1970-01-01
  • 2012-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多