Поле «Значение» может быть одного из четырех различных типов - лучший дизайн? - PullRequest
3 голосов
/ 13 января 2010

У меня есть класс с именем «DataModel» или что-то, что в основном представляет собой единицу данных, которая может быть либо строкой, либо числом, либо датой, либо логическим значением с различными (идентичными) атрибутами.

Как лучше написать эту модель?

  1. Имеют значение типа Object

    interface DataModel {
       Object getValue();  // cast to whatever is needed
       int getValueType(); // uses four constants
    }
    
  2. Имеют четыре разные реализации "StringModel", "NumberModel" и т. Д., Каждая со своим собственным типизированным методом "getValue ()". Это означает, что если у вас есть DataModel, вам нужно привести к правильной модели, чтобы получить значение.

    interface DataModel {
       int getValueType();
    }
    interface NumberDataModel extends DataModel {
      Integer getValue();
    }
    ...
    
  3. Имеется четыре различных метода, каждый из которых вызывает исключение, если вызывается для неправильного типа значения:

    interface DataModel {
      String getStringValue();
      Integer getIntegerValue();
      ...
      int getValueType();
    }
    
  4. Используйте дженерики. Это имеет обратную сторону, что у меня теоретически может быть любой объект любого типа ... с другой стороны, я мог бы просто выбросить исключение IllegalStateException в конструктор, если бы T не был одним из 4 разрешенных типов ...

    interface DataModel<T> {
      T getValue();
    }
    
  5. Это не имеет значения. Любой из вышеперечисленных. ;)

Ответы [ 5 ]

3 голосов
/ 13 января 2010

4 кажется лучшим - даже если вы не хотите реализовывать какой-либо старый тип, нет особой причины, почему вы не должны теоретически это разрешать - это не будет мешать чему-либо еще, что вы делаете.

2 голосов
/ 13 января 2010

4 - лучший ответ. Вы получите гибкость и удобство.

С другой стороны, если вы действительно хотите ограничить тип, вы можете сделать это с помощью комбинации 4 и 2. Например:

interface DataModel<T> {
   T getValue();
}
interface NumberDataModel extends DataModel<Number> {
   // empty
}
class NDM implements NumberDataModel {
   Number getValue() { return ... }
}

Затем вы можете сделать интерфейс DataModel защищенным / по умолчанию.

2 голосов
/ 13 января 2010

Ничего из перечисленного.

Так как вам нужно знать класс, чтобы сделать что-то разумное с результатом getValue (), в этом случае нечего получить от общего интерфейса, содержащего методы, аналогичные getValue ().

Имейте базовый класс с общими частями, затем подкласс и добавьте то, что вам нужно. Вы можете использовать instanceof вместо getValueType (), но если вам действительно нужен getValueType, его можно добавить в базовый класс как абстрактный метод. Если вы используете getValueType, я бы предложил, чтобы он возвращал перечисление, а не int.

2 голосов
/ 13 января 2010

1 хорошо, если вы не планируете добавлять много / любые новые типы И вам не нужно позволять третьим сторонам добавлять свои собственные типы. Вероятно, я бы использовал enum, а не int.

Я не вижу большого преимущества от 2 до 4. 4 является универсальным, хотя вы можете захотеть включить getValueType () даже в общем случае, чтобы вы могли иметь код, запрашивающий тип во время выполнения, часто полезный.

Я не думаю, что 3 - правильный путь, если только контент вашей модели не поддерживает извлечение разными способами (как это делает JDBC), но я не думаю, что это так.

Из прошлого опыта я бы сделал 4 и добавил к нему getValueType ().

0 голосов
/ 13 января 2010

В качестве общей альтернативы вы можете использовать литералы класса в качестве токенов типа времени выполнения и использовать newInstance() для получения безопасных типов ваших данных элементы. Это позволяет выполнять проверку во время компиляции через общие параметры и проверку во время выполнения через isAssignableFrom().

...