StructureMap и фабричные классы

#c# #structuremap

#c# #structuremap

Вопрос:

В моем коде есть классы som factory, и мне не нравится вызов Structermap. Как мне сделать это правильно?

 class ManagerBaseFactory
{
    public ManagerBase GetInstance(SomeEnum e)
    {
        Type t;

        switch (e)
        {
            case SomeEnum.A:
                t = typeof(Manager1);
            case SomeEnum.B:
                t = typeof(Manager2);
            case SomeEnum.C:
                t = typeof(Manager3);
        }
        return (ManagerBase)StructureMap.ObjectFactory.GetInstance(t);
    }
}
  

Ответ №1:

Я не вижу проблемы в использовании structure map таким образом. Что вам не нравится в текущем решении?

Вы могли бы использовать именованные экземпляры, для которых вам даже фабрика больше не понадобится. ссылка на structuremap

 public class ServicesRegistry : Registry
{
    public ServicesRegistry()
    {
        For< ManagerBase >().Use< Manager1 >().Named("A");
        For< ManagerBase >().Use< Manager2 >().Named("B");
        For< ManagerBase >().Use< Manager3 >().Named("C");
    }
}
  

Вы можете получить правильный, просто вызвав

 SomeEnum e = SomeEnum.A;
ObjectFactory.GetNamedInstance<ManagerBase>(e.ToString());
  

Комментарии:

1. ManagerBaseFactory не поддается тестированию. Я хотел бы иметь только один вызов Structermap в основном.

2. Я не уверен, что понимаю эту конфигурацию. Как выглядит процессор?

3. Это всего лишь пример, который я взял из руководства по структуре, и процессор не важен. Дело в том, что вы можете использовать structure map для получения конкретного экземпляра объекта (в вашей базе car ManagerBase) по имени, которое в вашем случае может быть значением перечислителя. Я обновил пример, чтобы он соответствовал вашим конкретным потребностям, я думаю.

4. Спасибо, теперь я понял. Все еще у меня проблема с вызовом Structermap на всех моих фабриках.

5. Если вы говорите о том факте, что ManagerBaseFactory не поддается тестированию, это зависит от того, что вы хотите протестировать. Но вы можете настроить structuremap так, чтобы возвращать фиктивные объекты ваших обычных экземпляров, что является одной из причин использования библиотеки, подобной structuremap.

Ответ №2:

Если вы абсолютно не хотите, чтобы ваш ManagerBaseFactory использовал контейнер, вы могли бы предоставить ему доступ ко всем реализациям ManagerBase из контейнера и заставить его вернуть правильную. Когда StructureMap видит IEnumerable типа в конструкторе, он вводит все экземпляры этого типа, о которых он знает.

 class ManagerBaseFactory
{
    private readonly IEnumerable<ManagerBase> _managers;

    public ManagerBaseFactory(IEnumerable<ManagerBase> managers)
    {
        _managers = managers;
    }

    public ManagerBase GetInstance(SomeEnum e)
    {
        Type t;

        switch (e)
        {
            case SomeEnum.A:
                t = typeof(Manager1);
                break;
            case SomeEnum.B:
                t = typeof(Manager2);
                break;
            case SomeEnum.C:
                t = typeof(Manager3);
                break;
            default:
                return null;
        }
        return _managers.FirstOrDefault(m => m.GetType().Equals(t));
    }
}
  

И, конечно, вам нужно убедиться, что все ваши реализации ManagerBase загружены в контейнер:

 var container = new Container(x =>
{
    x.Scan(scan =>
    {
        scan.TheCallingAssembly();
        scan.AddAllTypesOf<ManagerBase>();
    });
});
  

Ответ №3:

Не ясно, против какой части вы возражаете. Одно из улучшений, которое вы могли бы внести, — это иметь ManagerBaseFactory take в IContainer своем конструкторе, который вы можете использовать вместо ObjectFactory статического шлюза. Затем вы можете извлечь ManagerBaseFactory из контейнера, и контейнер сам внедрится в фабрику. Фабрику будет проще тестировать без статической зависимости.

Вы также могли бы поместить операторы return непосредственно в switch, чтобы вы могли воспользоваться преимуществами generics:

 case SomeEnum.A:
  return _container.GetInstance<Manager1>();
  

Комментарии:

1. ManagerBaseFactory не поддается тестированию, поскольку он использует контейнер DI.

2. Это абсолютно тестируемо. Вот почему я предложил использовать IContainer вместо ObjectFactory. В своем тесте создайте новый экземпляр Container, загрузите в него минимум, необходимый для тестируемого сценария, и передайте его в ManagerBaseFactory. Или просто отключите IContainer.