Я хотел бы использовать Dependency Injection (Castle Windsor), чтобы заменить какой-то заводской код, который я унаследовал Я правильно выбрал подход? - PullRequest
0 голосов
/ 17 ноября 2010

Вот как выглядит текущий код:

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 для этого конкретного приложения)

Редактировать:

Предложение Кшиштофа Кожича:

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 ]

2 голосов
/ 17 ноября 2010

Отвечая на вопросы о внедрении зависимостей здесь, в 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");
    }
}

Возможно, это полезно для вашего приложения.

2 голосов
/ 17 ноября 2010

Вы можете использовать Typed Factory с пользовательским селектором (см. этот пост для примера и документации).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...