2016-07-05 3 views
7

У меня есть (растущий) список Data-Generators. Генератор, который мне нужен, создается фабричным классом. Генераторы реализуют общий интерфейс, который включает, помимо прочего, статическую строку name.Factory Pattern, выбрав по свойству

Что мне нужно сделать: вызвать метод factory.Create со строковым параметром для вышеупомянутого имени. Метод create находит генератор с этим именем и возвращает новый экземпляр указанного генератора.

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

Вопрос:

  1. Это хороший способ справиться с этой проблемой?
  2. Как я могу найти все генераторы? Отражение по каждой реализации интерфейса/каждого члена пространства имен (уникальное для генераторов + их интерфейс)?
  3. Правильно ли назвать этот способ работы на заводе или это какой-то другой шаблон?

В конце концов, я бы назвал завод, как это (упрощенный):

//Caller 
public DataModel GetData2() 
{ 
    var generator = new DataFactory().Create("Gen.2"); 
    return generator.GetData(); 
} 

//Factory 
public class DataFactory 
{ 
    public AbstractDataGenerator Create(string type) 
    { 
     //Here the magic happens to find all implementations of IDataGenerator 
     var allGenerators = GetImplementations(); 
     var generator = allGenerators.FirstOrDefault(f => f.name == type); 
     if (generator != null) 
      return (AbstractDataGenerator)Activator.CreateInstance(generator); 
     else 
      return null; 
    } 
} 

//Interface 
public abstract class AbstractDataGenerator 
{ 
    public static string name; 
    public abstract DataModel GetData(); 
} 

//Data-Generators 
public class DataGen1 : AbstractDataGenerator 
{ 
    public static string name = "Gen.1"; 
    public DataModel GetData() 
    { 
     return new DataModel("1"); 
    } 
} 
public class DataGen2 : AbstractDataGenerator 
{ 
    public static string name = "Gen.2"; 
    public DataModel GetData() 
    { 
     return new DataModel("2"); 
    } 
} 

Если на заводе магия GetImplementations() быть сделано с помощью отражения или как-то другое? Должен ли я использовать совершенно другой подход?

Поскольку ответы относятся к IoC и DI: этот проект уже использует NInject, поэтому он будет доступен. Переключен от интерфейса к абстрактному классу.

+0

Один вопрос, который я нашел такого подхода заключается в том, что для того, чтобы получить свойство «Name» генератора вы _need экземпляр object_. – stuartd

+0

Вот почему я сделал свойство статическим. Это должно устранить необходимость в экземпляре, если я не ошибаюсь. – MilConDoin

+0

Интерфейсы не могут содержать статические элементы. – stuartd

ответ

4

Это хороший способ справиться с этой проблемой?

Имея фабрику, чтобы получить экземпляр класса логики, который вам нужен каким-то ключом, я считаю, что это хороший способ. Это образец, который я использую сам. О том, как у вас есть свой ключ - я бы предпочел, чтобы он не был членом static (независимо от того, что интерфейсы не могут иметь статические элементы), но точно так же, как property и добавить базовый класс в IDataGenerator. Этот базовый класс будет иметь конструктор, который получит name. Таким образом, каждый новый DataGenerator, который вы создаете, должен будет установить его, и вы не забудете.


О имея name как string - лично я предпочитаю иметь его «сильно типизированных».Я имею в виду, что если я передам Gen . 2 вместо Gen.2 со строками, я открою эту проблему только во время выполнения. Другие возможные способы (если вы хотите, потому что простая строка это тоже хорошо - дело вкуса):

  • Заменить строки с enum
  • Есть статический класс со статическими только для чтения строк для всех значений - то в вашем коде используйте эти значения. Вы получаете преимущества intellisense и не получаете строку неправильно, но лучше, чем перечисление - вы можете просто передавать строки, которые не входят в «список», поэтому вы можете добавлять новые в качестве надстроек.
  • Имейте объект RequestGenerator, с каждым Generator является IDataGenerator<TGeneratorRequest>. Это может быть излишним, но если у вас есть дополнительная информация, необходимая для создания DataGenerator, которая отличается от них, тогда рассмотрите ее.

Как я могу найти все генераторы? Отражение по каждой реализации интерфейса/каждого члена пространства имен (уникальное для генераторов + их интерфейс)?

Да, отражение может быть хорошим способом сделать это. Однако я бы предложил читать, например, Dependency Injection и IoC Containers, например Castle Windsor. Есть вещи, которые там уже реализуют это для вас, так зачем заново изобретать колесо :)

DI является концепция смены жизни, на мой взгляд

ли правильно называть этот способ работы фабрика, или это какой-то другой шаблон?

Yap. Это Factory

Должно ли волшебство GetImplementations() на заводе производить через Reflection или как-то иначе?

См ответ на вопрос 2

+0

Ваша добавочная идея со статическим классом, содержащим статические константные строки, уже реализовано :) Я использовал магическую строку в моем примере выше из простоты. – MilConDoin

+0

Хорошо :). Я бы также сказал, что способ выбора вашего «ключа» для фабрики отличается от того, как этот код подвергается. Если он скрыт за фасадом и представляет собой вашу собственную деталь реализации, то на самом деле не имеет значения, какой вариант. Однако, если он каким-то образом открыт для внешнего использования, я бы предпочел опции «enum» или «TRequest» - более понятный интерфейс, чем «строка» для других –

2

Это то, что инжектор конструктора может ДЕЙСТВИТЕЛЬНО сиять. Изучите инструменты инъекций зависимостей и используйте их! Он также проверяет ваш запрос «Бонус».

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

public class DataFactory 
{ 
    private Dictionary<string, IDataGenerator> generators; 

    public DataFactory(IDataGenerator[] generatorReferences) 
    { 
     this.generators = generatorReferences 
      .ToDictionary(k => k.name, v => v); 
    } 
    public IDataGenerator Create(string type) 
    { 
     IDataGenerator generator = null; 
     this.generators.TryGetValue(type, out generator); 
     return generator; 
    } 
} 

программного обеспечения Большинство DI имеет возможность автоматического сканирования узлов для реализации определенного типа (например, IDataGenerator) и зарегистрировать тех, с самими собой, когда он создает экземпляр вашего DataFactory, он будет автоматически включать их.

+0

Я использую NInject. Просто иметь Array интерфейса в конструкторе недостаточно, поэтому мне, вероятно, придется сделать привязку раньше. Теперь, чтобы убедиться, что есть простой способ сделать это, без необходимости делать расширенное отражение, которое я мог бы сделать напрямую без NInject. – MilConDoin

+0

Я использую Ninject в течение многих лет, массив будет работать нормально. Просто зарегистрируйте все экземпляры, которые у вас есть, и они будут введены.Если я правильно помню, поддерживаются следующие [], IEnumerable , List . Обратите внимание, что при вводе через IEnumerable он лениво загружается и переоценивается на каждом перечислении, поэтому будьте осторожны с этим. –

+0

Есть ли простой и автоматический способ регистрации всех экземпляров, которые реализуют интерфейс/выводятся из родительского класса? Я не так хорош с NInject. – MilConDoin