我想使用依赖注入(Castle Windsor)来替换我继承的一些工厂代码。我采取了正确的方法吗?

时间:2010-11-17 09:27:51

标签: dependency-injection castle-windsor

这是当前代码的样子:

public static class WidgetFactory
{
   public static AbstractWidget CreateWidget(WidgetSpec spec)
   {
        if (spec.ModelNo == "FOO")
            return new FooWidget(spec);

        if (spec.ModelNo == "BAR")
            return new BarWidget(spec);

        if (spec.ModelNo == "BOO")
            return new BooWidget(spec);
   }
}

这是我使用DI的实现:

的app.config

<components>
  <component id="FOO" 
             service="MyCo.App.AbstractWidget" 
             type="MyCo.App.FooWidget, MyApp" 
             lifestyle="transient" />
   <component id="BAR" 
             service="MyCo.App.AbstractWidget" 
             type="MyCo.App.BarWidget, MyApp" 
             lifestyle="transient" />
    ....        

</components>

代码

static class WidgetFactory
{
    static IWindsorContainer _container = 
        new WindsorContainer(new XmlInterpreter(new ConfigResource("castle")));

    public static AbstractWidget CreateWidget(WidgetSpec spec)
    {
        return _container.Resolve<AbstractWidget>(spec.ModelNo, new { widgetSpec = spec });
    }
}

这是正确的做法吗?我在忽视/做错/误会是什么?我应该为抽象类创建接口并从工厂返回它们吗?

(我更倾向于坚持使用这个特定应用程序的XML配置)

编辑:

KrzysztofKoźmic的建议:

public interface IFactory
{
    AbstractFactory CreateWidget(WidgetSpec widgetSpec);
    void ReleaseWidget(AbstractFactory widget);
}

public class CustomTypedFactoryComponentSelector : DefaultTypedFactoryComponentSelector
{
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        WidgetSpec widgetSpec = arguments[0] as WidgetSpec;
        if (method.Name == "CreateWidget" && arguments.Length == 1 && widgetSpec != null)
        {
            // The component mappings are stored as config settings
            // for the sake of example
            var componentName = Properties
                    .Settings
                    .Default
                    .Properties[widgetSpec.ModelNo]
                    .DefaultValue.ToString();

            return componentName;
        }

        return base.GetComponentName(method, arguments);
    }
}


container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IFactory>().AsFactory(c => c.SelectedWith(new CustomTypedFactoryComponentSelector())));
//...
var factory = container.Resolve<IFactory>();
var widgetFactory = factory.CreateWidget(widgetSpec);

2 个答案:

答案 0 :(得分:2)

您可以将Typed Factory与自定义选择器一起使用(有关示例和文档,请参阅this post)。

答案 1 :(得分:2)

在SO上回答有关依赖注入的问题时,我几乎总是说:“使用工厂”。我认为你的解决方案看起来很不错; - )

但也许还有一些改进空间。

因为工厂是静态类型,所以你别无选择,直接从代码中调用它。这使得很难测试该代码(如果可测试性当然是一个问题)。您可能尝试的是将工厂注入您正在使用的类型中的依赖项。因此,不要对静态类型进行硬依赖,而是在接口上创建依赖项。这看起来像这样:

public interface IWidgetFactory
{
    AbstractWidget CreateWidget(WidgetSpec spec);
}

internal class WidgetFactory : IWidgetFactory
{
   // code
}

现在您可以通过其界面轻松注册此类型:

<component
    service="MyCo.App.IWidgetFactory, MyApp" 
    type="MyCo.App.WidgetFactory, MyApp" 
    lifestyle="singleton" />

现在你可以从容器中请求IWidgetFactory,或者更好的是,在需要使用它的类型上注入IWidgetFactory作为构造函数参数:

public class TypeUsingWidgets
{
    private IWidgetFactory widgetFactory;

    public TypeUsingWidgets(IWidgetFactory widgetFactory)
    {
        this.widgetFactory = widgetFactory;
    }

    public void MethodUsingWidgets()
    {
        var widget = this.factory.CreateWidget("Foo");
    }
}

也许这对您的应用程序有益。