开发者

I'd like to use Dependency Injection (Castle Windsor) to replace some factory code I've inherited. Am I taking the correct approach?

This is what the current code looks like:

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);
   }
}

This is my implementation that uses 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>

Code

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 });
    }
}

Is this the correct approach? What am I overlooking/doing wrong/misunderstanding? Should I create interfaces for the abstract classes and return them from the factory instead?

(I would prefer to stick to XML configuration for this particular application)

Edit:

Suggestion by Krzysztof Koź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 Widget开发者_JS百科Spec;
        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);


You could use Typed Factory with custom selector (see this post for an example and the documentation).


When answering questions about dependency injection here on SO, I almost always say: "use a factory". I think your solution looks pretty good ;-)

Perhaps there still is some room for improvement, though.

Because the factory is a static type, you have no choice than calling that directly from code. This makes it hard to test that code (if testability is a concern of course). What you might try is to inject the factory as a dependency in the types you are using. So instead of having a hard dependency on a static type, create a dependency on an interface. This could look like this:

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

internal class WidgetFactory : IWidgetFactory
{
   // code
}

Now you can easily register this type by its interface:

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

Now you can request an IWidgetFactory from the container, or even better, inject the IWidgetFactory as constructor argument on the types that need to use it:

public class TypeUsingWidgets
{
    private IWidgetFactory widgetFactory;

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

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

Perhaps this is beneficial for your application.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜