2013-09-26 2 views
2

В настоящее время я пытаюсь написать фабричный класс, который не полагается на местоположение службы.Заводская модель без сервисного локатора

Единственная альтернатива, которую я могу придумать, это использовать инжектор конструктора для ввода всех возможных экземпляров, но это может привести к неожиданностям, поскольку классы передаются через ссылку. Возможно, это будет дорого и беспорядочно, если число возможных поставщиков растет.

Поставщики сами представляют собой комплексные классы, которые имеют свои собственные зависимости, поэтому ручная конструкция не соответствует действительности.

Обновлено место службы Пример:

public class ProviderFactory : IProviderFactory 
    { 
     private readonly IProviderConfigurationService _providerConfigurationService; 

     public enum SearchType 
     { 
      Foo, 
      Bar 
     } 

     public ProviderFactory(IProviderConfigurationService providerConfigurationService) 
     { 
      _providerConfigurationService = providerConfigurationService; 
     } 

     public Collection<IProvider> GetProviderInstances(SearchType searchType) 
     { 
      // Provider configuration service will read a XML/DB store to retrieve list of search providers applicable for a search type 
      var providerList = _providerConfigurationService.GetProviderList(searchType); 
      return new Collection<IProvider>(providerList.ForEach(x=> ServiceLocator.GetInstance(typeof(x))).ToList()) ; 
     } 
    } 

Каковы мои другие варианты? В настоящее время я использую Unity для DI.

+0

Зачем вам нужно так много объекта зависимостей в первую очередь? – Muctadir

+0

В зависимости от типа поиска мне нужно будет вызвать другой набор поставщиков. Это также действительный вопрос о фабричной схеме в целом, поскольку задача заключается в создании конкретных примеров, которые противоречат принципам IoC. –

+0

Какая структура DI вы используете? Ninject имеет расширение Факторий, которое является фантастическим для этого. –

ответ

2

Альтернативой передать Func<Type, object> конструктору и реализовать функцию через контейнер:

unity.RegisterInstance<Func<Type, object>>(t => unity.Resolve(t)) 

Тогда в классе:

public ProviderFactory(Func<Type, object> createFunc, IProviderConfigurationService pcs) 
{ 
    _createFunc = createFunc; 
} 

public Collection<IProvider> GetProviderInstances(SearchType searchType) 
{ 
    var providerList = _providerConfigurationService.GetProviderList(searchType); 
    return new Collection<IProvider>(providerList.Select(_createFunc).ToList()); 
} 
+0

Спасибо @Bojan, но похоже, что он просто маскирует передачу контейнера DI, вокруг которого действительно попадает в серое место расположения службы, и тем более, что Func не так понятен в том, что он должен делать. –

+0

Я согласен, что не ясно, что он должен делать немедленно, но также легко объявить делегата с более описательным именем (например, «public object delegate CreateProvider (Type providerType)») и использовать это вместо 'Func'. Однако здесь нет места обслуживания, как это обычно понимается. Любой метод должен использовать контейнер DI под капотом, и я не думаю, что это обязательно плохо, но использование делегата вместо этого позволяет более удобное тестирование. –

+0

Я думаю, что путь делегата попадает в джек-пот. Он инкапсулирует разрешение типа от остальной части кода, а делегат назван свойством. Для дополнительных точек я строго набрал делегата, чтобы вернуть IProvider, чтобы вы не могли злоупотреблять им так же легко. –

1

Вам не хватает абстракции.

Ваш ProviderFactory должен реализовать абстракцию IProviderFactory. Таким образом вы можете разместить этот интерфейс в базовой библиотеке своего приложения, и вы можете разместить реализацию ProviderFactory внутри своего Composition Root. Для кода, который живет внутри вашего корня композиции, нормально ссылаться на библиотеку DI, и в этом случае you're not using service location.

+0

Так вы выступаете за то, что конкретный ProviderFactory живет в Global.asax (это проект WebAPI) и напрямую ссылается на контейнер DI или я что-то неправильно читаю ? ProviderFactory содержит бизнес-логику, и я не уверен, что это уместно. –

+0

Да, я сторонник того, что все классы, которые ссылаются на контейнер, живут в корне композиции. Я не защищаю то, что вы размещаете бизнес-логику внутри своего корня композиции. Убедитесь, что бизнес-логика размещена в вашем приложении и не ссылается на контейнер. Вы должны соответствующим образом изменить свой дизайн. – Steven

+0

Есть ли простой пример, который вы можете показать? Я не могу представить ваше решение, которое не связано с нормальной регистрацией IProviderFactory с моим контейнером DI на этапе начальной загрузки или передачей контейнера DI в ProviderFactory через инсталляцию конструктора. –

0

Я недавно решенной очень похожий вопрос в моем собственном коде с использованием рамки DI. Чтобы удовлетворить Dependency Inversion, фабричный конструктор должен принять интерфейс (как говорили другие ответы), но для того, чтобы заставить фреймворк вводить правильный тип, сложно, не имея массивного списка аргументов, подробно описывающих каждую возможную конкрецию.

SimpleInjector позволяет регистрировать все конкреции данной абстракции с:

Container.RegisterCollection(typeof(IProvider), new [] {typeof(TKnown).Assembly,...}); 

Ваш XML может перечислить (возможно, внешние) узлы, где определяются конкреции, и вы могли бы построить массив сборки оттуда. Тогда ваша фабрика просто должна принять их всех и выбрать один, возможно, на основе указанного вами типа поиска.

public class ProviderFactory 
{ 
    private List<IProvider> providers; 
    public ProviderFactory(IEnumerable<IProvider> providers) 
    { 
     this.providers = providers.ToList(); 
    } 

    public IProvider GetProvider(string searchType) 
    { 
     // using a switch here would open the factory to modification 
     // which would break OCP 
     var provider = providers.SingleOrDefault(concretion => concretion.GetType().Name == searchType); 

     if (provider == null) throw new Exception("No provider found of that type. Are you missing an assembly in the RegisterCollection for IProvider?"); 

     return provider; 
    } 

Я знаю, что я путь поздно к партии по этому вопросу, но при условии, другие люди не видят такой подход как проблематично, это может быть полезно.

Смежные вопросы