Лучший способ создания объектов из разных форматов данных - PullRequest
2 голосов
/ 27 марта 2012

Для проекта, над которым я работаю, мне нужно создавать объекты, используя разные форматы исходных данных, более или менее как это:

public class FooService
{
    protected DataFormat Format { get; set; }

    public FooService(DataFormat format = DataFormat.Json)
    {
        Format = format;
    }

    public Foo GetFoo(int id)
    {
        string content = GetContentInFormat("someuri/foo/" + id, Format);

        // Something here must create a "Foo" object
        // based on Format, which could be Json, Xml or other
    }
}

public enum DataFormat
{
    Json,
    Xml
} 

Я знаю, что мог бы:

1) Используйте разные методы и выберите правильный в зависимости от формата:

switch (Format)
{
    case DataFormat.Xml:
       return CreateFooFromXml(content);
    case DataFormat.Json:
       return CreateFooFromJson(content);
    default:
       throw new Exception("Invalid format.");
}

Минусы: будет создано не менее 8 различных типов таким образом, поэтому мне нужно что-то более расширяемое и поддерживаемое.

2) Сделать FooService интерфейсом или абстрактным классом и реализовать конкретные классы, по одному для каждого формата.

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

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

Ответы [ 5 ]

3 голосов
/ 27 марта 2012

Вместо того, чтобы форматировать перечисление, вы можете сделать его интерфейсом (IFormat).

Затем для каждого формата создать конкретный класс (например, JsonFormat), который реализует IFormat.

Каждый конкретный класс должен иметь только то, что является уникальным для конкретного формата, например, как найти элемент / запись по Id.

Это «шаблон стратегии»:http://en.wikipedia.org/wiki/Strategy_pattern

2 голосов
/ 27 марта 2012

Вы явно говорите здесь паттерны творчества , но я не думаю, что мы знаем достаточно о сложности Foo, чтобы четко рекомендовать один другому.К счастью, творческих шаблонов GoF не так много, так что вы можете прочитать все из них в довольно короткие сроки.Похоже, вы уже понимаете шаблон фабричный метод , поэтому вы можете перейти к абстрактная фабрика и строитель , чтобы увидеть, соответствуют ли они требованиям.

Советы от вики-конструктора:

  • Построитель фокусируется на пошаговом построении сложного объекта.Абстрактная фабрика выделяет семейство объектов товара (простых или сложных).Builder возвращает продукт в качестве последнего шага, но что касается абстрактной фабрики, продукт возвращается немедленно.
  • Builder часто создает Composite .
  • Частопроекты начинаются с использования Factory Method (менее сложный, более настраиваемый, подклассы разрастаются) и переходят к Abstract Factory, Prototype или Builder (более гибкие, более сложные), когда дизайнер обнаруживает, где требуется большая гибкость.
  • Иногда шаблоны создания дополняют друг друга: построитель может использовать один из других шаблонов для реализации того, какие компоненты создаются.Abstract Factory, Builder и Prototype могут использовать Singleton в своих реализациях.
  • Builders являются хорошими кандидатами на свободный интерфейс .

Надежда, которая помогает.

1 голос
/ 27 марта 2012

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

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

0 голосов
/ 27 марта 2012

Метод фабрики шаблонов - это то, что вам нужно. Сам шаблон не удалит оператор switch, но он будет существовать только в одном месте.

Однако вы можете объединить шаблон с небольшим количеством отражения, чтобы удалить оператор switch.

Код (псевдокод)

public class FormatterFactory
{
  Dictionary<string, IFormatter> _formatters;

  public void FormatterFactory()
  {
      var baseType = typeof(IFormatter);
      var formatterTypes = AppDomain
            .GetExecutingAssembly()
            .GetTypes.Where(x=>baseType.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract);

       // convention based. Each formatter must be named like "JsonFormatter"
       foreach (var type in formatterTypes) 
       {
           _formatters.Add(type.Name.Replace("Formatter", "").ToLower(), type), 
       }
  }
  public IFormatter Create(string formatName)
  {
      var type = _formatters[formatName.ToLower());
      return (IFormatter)Activator.CreateInstance(type);

  }
}

Использование:

var factory = new FormatterFactory();
var json = factory.Create("json").Serialize(myObject);
0 голосов
/ 27 марта 2012

, в частности, вы могли бы использовать IOC (например, autofac) и какой-либо тип инъекций (как я вижу, вы уже близки к этому мышлению),
зарегистрировать все доступные форматы сервисов при запуске
(из конфигурации или просто простой одноразовой инициализации, которую легко поддерживать с течением времени при добавлении новых типов) ...
как например реализация интерфейса IOutputFormatService
Затем вы можете динамически округлять их (например, разрешать, перечислять все реализованные форматы)
и каждый из них может «выполнять работу» через общий интерфейс - или, если вы «знаете их», вы также можете обращаться к ним специально, например, к IJsonOutputFormatInterface.
Если / когда вы добавляете (или удаляете) новое - меняется только часть конфигурации
И как уже упоминалось, вам нужно всего лишь кодировать каждый сервис, чтобы делать то, что для него специфично, форматировать рендеринг и т. Д.
примечание: рефлексия не обязательна, важнее концепция

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