当一个API发展时,处理“int枚举”模式与Java枚举共存的最佳方法是什么?

时间:2008-12-04 15:35:50

标签: java api enums

假设您正在维护最初几年前发布的API(在java获得enum支持之前),并且它定义了一个枚举值为int的类:

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作为int并添加新{的每个方法的弃用版本。 {1}}每个版本。您还需要为每个已弃用的foo(VitaminType2 v)方法及其相应的foo(int v)方法编写测试,因此您只需将QA工作量增加。

最好的方法是什么?

5 个答案:

答案 0 :(得分:6)

API消费者将VitaminType与NutrientType混淆的可能性有多大?如果不太可能,那么维持API设计的一致性可能会更好,特别是如果建立了用户群并且您希望最小化客户所需的工作/学习的增量。如果可能出现混淆,那么NutrientType应该可能成为 enum

这不一定是一夜之间的彻底改变;例如,您可以通过枚举显示旧的int值:

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并使您可以控制何时弃用旧类型以及在内部依赖旧类型的任何代码中安排切换。

需要注意保持文字值与那些可能与旧的消费者代码相符的文字值同步。

答案 1 :(得分:3)

个人意见认为,尝试转换可能不值得。一方面,“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.

这是一个众所周知的习语,可能不会那么糟糕,留下它,肯定比替代方案更好。

答案 2 :(得分:1)

有传言说“make”的创建者意识到Makefile的语法很糟糕,但觉得他无法改变它,因为他已经有10个用户。

即使会伤害您的客户,不惜一切代价向后兼容也是一件坏事。因此无法真正为您提供有关如何处理案例的明确答案,但请务必考虑长期用户的成本。

另外想一想你可以重构代码核心的方法是将旧的基于整数的枚举仅保留在外层。

答案 3 :(得分:1)

等待下一个主要修订,将所有内容更改为枚举并提供脚本(sed,perl,Java,Groovy,...)以转换现​​有源代码以使用新语法。

显然这有两个缺点:

  • 没有二进制兼容性。这个有多重要取决于用例,但在新的主要版本的情况下是可以接受的
  • 用户必须做一些工作。如果工作足够简单,那么这也是可以接受的。

与此同时,将新类型添加为枚举,并将旧类型保留为整数。

答案 4 :(得分:0)

如果可能的话,最好的方法是修改已发布的版本。在我看来,一致性将是最佳解决方案,因此您需要进行一些重构。我个人不喜欢弃用的东西,因为它们已经开始了。您可能要等到更大的版本发布并在此之前使用这些整数,并重构一个大项目中的所有内容。如果这是不可能的,你可能会认为自己坚持使用整数,除非你创建了一些包装或其他东西。

如果没有任何帮助,但您仍然在不断改进代码,那么最终会失去一致性或者使用已弃用的版本。在任何情况下,通常至少在某些时候,如果人们失去了它的一致性并且从头开始创造新的东西,人们会厌倦旧东西......所以无论如何你将在未来进行重构。

如果出现问题,客户可能会废弃该项目并购买其他产品。通常不是客户的问题,你能否承担重构,他们只是购买适合他们的东西。所以最后这是一个棘手的问题,需要注意。