Отображение операторов переключения на классы данных - PullRequest
1 голос
/ 24 октября 2009

В моем коде это часто встречается, мне интересно, есть ли какой-нибудь способ удаления оператора switch или есть более элегантный способ сделать это?

    public class MetaData
{
    public string AlbumArtist { get; set; }
    public string AlbumTitle { get; set; }
    public string Year { get; set; }
    public string SongTitle { get; set; }


    public static MetaData CreateMetaDataFrom(IEnumerable<TextFrame> textFrames)
    {
        var metaData = new MetaData();

        foreach (var frame in textFrames)
        {
            switch (frame.Descriptor.ID)
            {
                case "TPE1":
                    metaData.AlbumArtist = frame.Content;
                    break;

                case "TALB":
                    metaData.AlbumTitle = frame.Content;
                    break;

                case "TIT2":
                    metaData.SongTitle = frame.Content;
                    break;

                case "TYER":
                    metaData.Year = frame.Content;
                    break;
            }
        }

        return metaData;
    }
}

Ответы [ 6 ]

3 голосов
/ 24 октября 2009

Это связано с объектно-ориентированным подходом. Обычный метод избавления от «если» или «дела» - это использование таблицы поиска критериев и эффектов. Существуют и другие методы, использующие эту же идею, такие как программирование, направленное на данные (http://en.wikipedia.org/wiki/Data-directed_programming) и таблицы диспетчеризации (http://en.wikipedia.org/wiki/Dispatch_table).). Многие реализации языка используют таблицы диспетчеризации типов для реализации вызовов виртуальных методов.

Таблица поиска может быть хеш-таблицей, заполненной лямбда-функциями следующим образом:

Dictionary<string, Func<MetaData, string, string>> lookup = new Dictionary<string, Func<MetaData, string, string>>();
lookup["TPE1"] = (m, v) => m.AlbumArtist = v;
lookup["TALB"] = (m, v) => m.AlbumTitle = v;
lookup["TIT2"] = (m, v) => m.SongTitle = v;
lookup["TYER"] = (m, v) => m.Year = v;

тогда вы назначаете поля метаданных внутри вашего цикла как:

lookup[frame.Descriptor.ID](metaData, frame.Content);
1 голос
/ 24 октября 2009

Из вашего кода я заключаю, что IEnumerable всегда имеет 4 члена так что вы можете просто написать (не пробовал, поэтому проверьте синтаксис):

public static MetaData CreateMetaDataFrom(IEnumerable<TextFrame> textFrames)
{
    return new MetaData()
    {
        metaData.AlbumArtist = textFrames.Where(frame => frame.Descriptor.ID = "TPE1").SingleOrDefault().Content,
        metaData.AlbumTitle = textFrames.Where(frame => frame.Descriptor.ID = "TALB").SingleOrDefault().Content, 
        metaData.SongTitle = textFrames.Where(frame => frame.Descriptor.ID = "TIT2").SingleOrDefault().Content;
        metaData.Year = textFrames.Where(frame => frame.Descriptor.ID = "TYER").SingleOrDefault().Content;
    };
}
1 голос
/ 24 октября 2009

Похоже, вы знаете, какие типы будут перед рукой (с помощью переключателя), так почему бы просто не получить значения, как требуется, без переключателя for.

Примеры использования хеш-таблицы, и вы знаете, какие поля будут доступны, просто используйте поля.

Если вы не уверены, доступно ли поле, простой тест будет достаточным, если список содержит значение.

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

1 голос
/ 24 октября 2009

Мне хотелось предложить шаблон стратегии, но вам может потребоваться небольшое изменение. Рассмотрим написание метода в классе TextFrame, давайте назовем его putContent (MetaData).

Затем создайте подклассы TextFrame, каждый из которых представляет отдельный тип фрейма. Каждый подкласс переопределяет метод putContent (Metadata) и выполняет соответствующую логику.

Пример псевдокода для TPE1:

 Metadata putContent(MetaData md){
       md.AlbumArtist = Content;
       return md;
 }

Ваш код метаданных изменится на:

var metaData = new MetaData();

    foreach (var frame in textFrames)
    {
           metaData = frame.putContent(metaData);
    }
 return metaData;

Конечно, для создания текстовых фреймов самим потребуется фабрика, так что это не конец истории.

1 голос
/ 24 октября 2009

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

0 голосов
/ 29 октября 2009

То, что у вас действительно есть, это сеттер с четырьмя путями. Канонический рефакторинг здесь - «Заменить параметр явными методами» (стр. 285 из Рефакторинг Мартина Фаулера). Пример Java, который он приводит, меняется:

void setValue(String name, int value) {
  if (name.equals("height")) {
    _height = value;
    return;
  }
  if (name.equals("width")) {
    _width = value;
    return;
  }
}

до:

void setHeight(int arg) {
  _height = arg;
}

void setWidth(int arg) {
  _width = arg;
}

Предполагая, что вызывающий CreateMetaDataFrom() знает, что он передает, вы можете пропустить switch/case и использовать фактические установщики для этих свойств.

...