Что такое хороший дизайн при попытке построить объекты из списка пар ключ-значение? - PullRequest
3 голосов
/ 08 сентября 2008

Поэтому, если у меня есть метод парсинга текстового файла и возврата списка из списка из пары значений и хотите создать объекты из возвращенного kvps (каждый список kvps представляет отдельный объект), какой метод будет лучшим?

Первый способ, который приходит на ум, довольно прост, просто держите список ключевых слов:

private const string NAME   = "name";
private const string PREFIX = "prefix";

и проверьте по ключам, которые я получаю, для констант, которые я хочу, определенные выше. Это довольно основная часть проекта, над которым я работаю, поэтому я хочу сделать это хорошо; есть ли у кого-нибудь более убедительные предложения (не говоря о том, что в описанном выше методе нет ничего по сути ненадежного - я просто спрашиваю вокруг)?

Edit:

Более подробная информация была запрошена. В свободное время я работаю над небольшой игрой и создаю игровой мир с помощью файлов конфигурации. Их четыре - одно определяет все существа, другое определяет все области (и их местоположение на карте), другое - все объекты, и последнее определяет различные параметры конфигурации и вещи, которые не подходят нигде. С первыми тремя файлами конфигурации я буду создавать объекты на основе содержимого файлов - это будет довольно много текста, поэтому будет много строк, таких как имена, множественные числа, префиксы - такого рода вещи. Значения конфигурации все такие:

-
key: value 
key: value
-
key: value
key: value
-

Где строка '-' обозначает новый раздел / объект.

Ответы [ 8 ]

3 голосов
/ 08 сентября 2008

Делая множество необоснованных предположений, я думаю, что наилучшим подходом было бы создание Фабрики, которая получит список пар ключ-значение и вернет соответствующий объект или сгенерирует исключение, если оно недопустимо (или создаст фиктивный объект, или что лучше в конкретном случае).

private class Factory {

   public static IConfigurationObject Factory(List<string> keyValuePair) {

       switch (keyValuePair[0]) {

          case "x":
              return new x(keyValuePair[1]);
              break;
          /* etc. */
          default:
              throw new ArgumentException("Wrong parameter in the file");
       }

  }

}

Самое сильное предположение здесь состоит в том, что все ваши объекты могут обрабатываться частично как один и тот же (т. Е. Они реализуют один и тот же интерфейс (в примере IConfigurationObject) или принадлежат одному и тому же дереву наследования).

Если они этого не делают, то это зависит от потока вашей программы и от того, что вы делаете с ними. Но тем не менее, они должны:)

РЕДАКТИРОВАТЬ: Учитывая ваше объяснение, у вас может быть одна Фабрика для каждого типа файла, переключатель в ней будет официальным источником разрешенных типов для каждого типа файла, и они, вероятно, имеют что-то общее. Отражение возможно, но оно более рискованно, потому что оно менее очевидно и самодокументировано, чем это.

3 голосов
/ 08 сентября 2008

Посмотрите на XmlSerializer . Даже если вы не можете использовать XML на диске, вам может потребоваться скопировать некоторые его функции. Тогда это может выглядеть так:

public class DataObject {
  [Column("name")]
  public string Name { get; set; }

  [Column("prefix")]
  public string Prefix { get; set; }
}

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

2 голосов
/ 08 сентября 2008

Для чего вам нужен объект? Как бы вы это ни описали, вы все равно будете использовать их как некую (по ключу) карту с ограниченным доступом. Если вам не нужно какое-то наследование, я бы просто обернул подобную карте структуру в объект, подобный этому:

[java-inspired pseudo-code:]<code>
class RestrictedKVDataStore {
   const ALLOWED_KEYS = new Collection('name', 'prefix');
   Map data = new Map();

   void put(String key, Object value) {
      if (ALLOWED_KEYS.contains(key))
          data.put(key, value)
   }

   Object get(String key) {
      return data.get(key);
   }
}
</code>
1 голос
/ 08 сентября 2008

РЕДАКТИРОВАТЬ:

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

List<List<KeyValuePair<String,String>>> itemConfig = 
    new List<List<KeyValuePair<String,String>>>();

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

СТАРЫЙ ПОСТ:

Вот небольшой хитрый способ сделать это с помощью отражения:

Основная идея:

  • Использовать общий базовый класс для каждого класса Object.
  • Поместите все эти классы в отдельную сборку.
  • Поместите эту фабрику в эту сборку тоже.
  • Передайте KeyValuePair, которую вы прочитали из вашей конфигурации, и взамен он найдет класс, соответствующий KV.Key, и создаст для него экземпляр с помощью KV.Value
   
      public class KeyValueToObjectFactory
      { 
         private Dictionary _kvTypes = new Dictionary();

        public KeyValueToObjectFactory()
        {
            // Preload the Types into a dictionary so we can look them up later
            // Obviously, you want to reuse the factory to minimize overhead, so don't
            // do something stupid like instantiate a new factory in a loop.

            foreach (Type type in typeof(KeyValueToObjectFactory).Assembly.GetTypes())
            {
                if (type.IsSubclassOf(typeof(KVObjectBase)))
                {
                    _kvTypes[type.Name.ToLower()] = type;
                }
            }
        }

        public KVObjectBase CreateObjectFromKV(KeyValuePair kv)
        {
            if (kv != null)
            {
                string kvName = kv.Key;

                // If the Type information is in our Dictionary, instantiate a new instance of that class.
                Type kvType;
                if (_kvTypes.TryGetValue(kvName, out kvType))
                {
                    return (KVObjectBase)Activator.CreateInstance(kvType, kv.Value);
                }
                else
                {
                    throw new ArgumentException("Unrecognized KV Pair");
                }
            }
            else
            {
                return null;
            }
        }
    }
1 голос
/ 08 сентября 2008

Можно создать интерфейс, соответствующий именам столбцов, а затем использовать API Reflection.Emit для создания типа во время выполнения, который предоставит доступ к данным в полях.

0 голосов
/ 08 сентября 2008

Это правда?

Да; Я обдумал это. От меня будет гораздо больше работы, чем нужно. : ')

0 голосов
/ 08 сентября 2008

... Это довольно основная часть проект, над которым я сейчас работаю ...

Это правда?

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

Тогда вы можете заняться тем, что имеет значение: игрой.

Просто мысль

0 голосов
/ 08 сентября 2008

@ Дэвид:
У меня уже есть парсер (и большинство из них будут написаны от руки, поэтому я решил отказаться от XML). Но похоже, я действительно хороший способ сделать это; Я должен проверить это. Отличная точка зрения о версии тоже.

@ Argelbargel:
Это тоже хорошо выглядит. : ')

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