Какой лучший способ справиться с сосуществованием шаблона int enum с перечислениями java по мере развития API? - PullRequest
5 голосов
/ 04 декабря 2008

Предположим, вы поддерживаете API, который был первоначально выпущен несколько лет назад (до того, как Java получил поддержку enum), и он определяет класс со значениями перечисления в виде целых чисел:

public class VitaminType {
 public static final int RETINOL = 0;
 public static final int THIAMIN = 1;
 public static final int RIBOFLAVIN = 2;
}

С годами API развивался и приобретал специфичные для Java 5 функции (обобщенные интерфейсы и т. Д.). Теперь вы собираетесь добавить новое перечисление:

public enum NutrientType {
 AMINO_ACID, SATURATED_FAT, UNSATURATED_FAT, CARBOHYDRATE;
}

Шаблон int-enum «старого стиля» не имеет безопасности типов, нет возможности добавления поведения или данных и т. Д., , но он опубликован и используется . Я обеспокоен тем, что смешивание двух стилей перечисления несовместимо для пользователей API.

Я вижу три возможных подхода:

  • Откажитесь и определите новое перечисление (NutrientType в моем вымышленном примере) как серию целых чисел, подобных классу VitaminType. Вы получаете согласованность, но не пользуетесь безопасностью типов и другими современными функциями.

  • Решите жить с несоответствием в опубликованном API: оставьте VitaminType как есть и добавьте NutrientType как enum. Методы, которые принимают VitaminType, все еще объявляются как принимающие int, методы, которые принимают NutrientType, объявляются как принимающие такие.

  • Устаревший класс VitaminType и введение нового перечисления VitaminType2. Определите новый NutrientType как перечисление.
    Поздравляем, в течение следующих 2-3 лет, пока вы не сможете убить устаревший тип, вы будете иметь дело с устаревшими версиями каждого отдельного метода, который принимает VitaminType в качестве целого числа, и добавляете новый foo(VitaminType2 v) версия каждого. Вам также нужно написать тесты для каждого устаревшего метода foo(int v) и соответствующего метода foo(VitaminType2 v), чтобы вы просто умножили свои усилия по обеспечению качества.

Каков наилучший подход?

Ответы [ 5 ]

6 голосов
/ 04 декабря 2008

Насколько вероятно, что потребители API перепутают VitaminType с NutrientType? Если это маловероятно, тогда, возможно, лучше поддерживать согласованность разработки API, особенно если пользовательская база установлена, и вы хотите минимизировать дельту работы / обучения, требуемую клиентами. Если возможна путаница, то NutrientType, вероятно, должен стать enum .

Это не должно быть оптовая ночная смена; например, вы можете выставить старые значения int через enum:

public enum Vitamin {

    RETINOL(0), THIAMIN(1), RIBOFLAVIN(2);

    private final int intValue;

    Vitamin(int n) {
        intValue = n;
    }

    public int getVitaminType() {
        return intValue;
    }

    public static Vitamin asVitamin(int intValue) {
        for (Vitamin vitamin : Vitamin.values()) {
            if (intValue == vitamin.getVitaminType()) {
                return vitamin;
            }
        }
        throw new IllegalArgumentException();
    }

}

/** Use foo.Vitamin instead */
@Deprecated
public class VitaminType {

    public static final int RETINOL = Vitamin.RETINOL.getVitaminType();
    public static final int THIAMIN = Vitamin.THIAMIN.getVitaminType();
    public static final int RIBOFLAVIN = Vitamin.RIBOFLAVIN.getVitaminType();

}

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

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

3 голосов
/ 04 декабря 2008

Личное мнение таково, что, вероятно, не стоит пытаться конвертировать. Во-первых, идиома «public static final int» в скором времени не исчезнет, ​​учитывая, что она широко распространена по всему JDK. С другой стороны, отслеживание использования оригинальных целых может быть очень неприятным, учитывая, что ваши классы скомпилируют ссылку, так что вы, вероятно, не будете знать, что что-то сломали, пока не стало слишком поздно (под которым я имею в виду

class A
   {
       public static final int MY_CONSTANT=1
   }

   class B
   {
           ....
           i+=A.MY_CONSTANT; 
   }

компилируется в

i+=1

Так что, если вы переписываете A, вы, возможно, никогда не поймете, что B сломан, пока не перекомпилируете B позже.

Это довольно известная идиома, вероятно, не так страшно оставлять ее, конечно, лучше, чем альтернатива.

1 голос
/ 04 декабря 2008

Дождитесь следующей основной ревизии, измените все на enum и предоставьте скрипт (sed, perl, Java, Groovy, ...) для преобразования существующего исходного кода в новый синтаксис.

Очевидно, что это имеет два недостатка:

  • Нет двоичной совместимости. Насколько это важно, зависит от вариантов использования, но может быть приемлемо в случае нового основного выпуска
  • Пользователи должны сделать некоторую работу. Если работа достаточно проста, то это тоже может быть приемлемым.

А пока добавляйте новые типы в качестве перечислений и сохраняйте старые типы в виде целых.

1 голос
/ 04 декабря 2008

Ходят слухи, что создатель "make" понял, что синтаксис Makefiles был плохим, но чувствовал, что не может его изменить, потому что у него уже было 10 пользователей.

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

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

0 голосов
/ 04 декабря 2008

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

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

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

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