【问题标题】:Extending Java Enums扩展 Java 枚举
【发布时间】:2012-01-10 16:05:35
【问题描述】:

我有一个关于处理 Java 枚举时代码重用的最佳设计模式的问题。基本上,我想要实现的是能够定义几个模拟静态业务集合(常量集)的枚举,但我也希望在它们之间共享行为,并且使用最少的编码。

从抽象类继承类很容易实现,但是,由于 Java 枚举不能扩展(它们只能实现接口),这种类型的工作很乏味,并且涉及很多容易出错的复制/粘贴工作(复制从枚举到枚举的代码)。应该在所有枚举之间共享的“业务逻辑”示例包括从/到字符串转换、实例和逻辑比较等。

我现在最好的方法是将辅助类与业务接口结合使用,但这仅能降低代码复杂性(因为所有枚举仍然必须声明和调用辅助类)。见例子(只是为了澄清):

public enum MyEnum {
    A, B, C;

    // Just about any method fits the description - equals() is a mere example
    public boolean equals(MyEnum that) {
        ObjectUtils.equals(this, that);
    }
} 

StackOverflowers 如何处理这种“语言特性”?

【问题讨论】:

  • 可以试试 Lombok 和 @Delegate,我从来没有用枚举尝试过。
  • 在 Java 6 中,枚举是最终类;你不能扩展它们。我相信java 7是一样的。
  • 枚举不是为此而生的:要么使用类(或带有枚举的类),要么分离逻辑;对于这样的情况,您可以使用工厂模式(如果枚举之间的行为足够不同)。
  • @DwB - 我知道枚举是最终的(问题中提到了)。我要问的是一种合法的 Java 设计模式,它可以缓解编写太多代码来解决这个问题的问题,从而保持 DRY。
  • @DaveNewton - Lombok 看起来很有趣(类似于 Spring Roo 的概念?)但它在构建链中引入了另一个工具。当前版本是 0.10.6,这对我来说是不行的。只是好奇,您是否自己将其部署到生产环境中?

标签: java enums


【解决方案1】:

您可以将可重用逻辑移至专用(非枚举)类,然后将枚举委托给这些类。这是一个例子:

[旁注:不推荐继承PlusTwo extends PlusOne(b/c PlusTwo 不是 PlusOne)。这里只是为了说明能够扩展现有逻辑的意义。]

public interface Logic {
  public int calc(int n);
}

public static class PlusOne implements Logic {
  public int calc(int n) { return n + 1; }
}

public static class PlusTwo extends PlusOne {
  @Override
  public int calc(int n) { return super.calc(n) + 1; }
}

public static enum X {
  X1, X2;   
  public Logic logic;

  public int doSomething() { 
    return logic.calc(10);
  }
}

public static enum Y {
  Y1, Y2;
  public Logic logic;

  public String doSomethingElse() { 
    return "Your result is '" + logic.calc(10) + "'";
  }
}

public static void main(String[] args) {
  // One time setup of your logic:
  X.X1.logic = new PlusOne();
  X.X2.logic = new PlusTwo();  
  Y.Y1.logic = new PlusOne();
  Y.Y2.logic = new PlusTwo();

  System.out.println(X.X1.doSomething());
  System.out.println(X.X2.doSomething());
  System.out.println(Y.Y1.doSomethingElse());
  System.out.println(Y.Y2.doSomethingElse());
}

【讨论】:

  • 最后,这只是我最初提议的一个变体。它只是解耦了实现逻辑。我也会为Logic 使用工厂,而不是像那样暴露它(我相信你也会这样做,如果这只是一个例子)。
【解决方案2】:

我也会这样做,或者将枚举组合成一个超级枚举。

使用 Java 8,这将变得更容易。您将能够为接口方法定义一个default 实现并让enum 扩展接口。

【讨论】:

  • enums 组合成一个超级enum 是什么意思?即使没有意义,也可以比较值 A 和 B,这不是不安全的吗?
  • 这可能不安全,这取决于上下文。
  • 最后,所有其他解决方案都在不违反 DRY 原则的情况下增加了更多复杂性,所以这似乎是解决方案。总有一天,我的手指扩展类来到 Java...
【解决方案3】:

我很少发现 enums 有用,除了表示在这种情况下它们不需要行为的有限状态。

我建议使用Factory 将需要行为的enums 重构为classes

【讨论】:

  • 枚举比使用常量传递值要安全得多,并且更易于操作(EnumSet<...>、switch 等)。我同意它们比 C# 对应的功能要弱得多,但它们仍然有自己的位置(尤其是当您只是重构一些代码并且无法进行完全重写时:))。
  • 我同意enums 比旧的final static int CONSTANT = 1 更好,而且语法糖很好,但每次我把行为放在enum 中我都后悔了。
  • @Isoliveira 等等 LESS 比基本上是围绕 int 的语法糖的 c# 枚举强大吗?你是怎么得出这个结论的?无论如何,我在某种程度上同意 Garett:枚举是用来表示有限状态的,在这种情况下,我不确定为什么我们需要扩展它们。但是在枚举中拥有一些简单的(!)状态通常是有用的 imo。如果它不仅仅是简单的方法,它应该是一个类。但是例如getComplement() 方法在我的书中会很好。
  • @Voo - 我想说这正是enums 应该是的,而不是Java 方法的final 类周围既不存在也不存在语法糖。作为班级,enum 被滥用以容纳各种东西,在恕我直言,这是一件消极的事情(我想我所问的问题也可能被视为对enums 的滥用)。无论哪种方式,我的意思是“强大”,因为“在这种情况下,语言将允许我通过扩展类做我需要的事情”。鉴于这是另一种语言功能,而不是 enums 特有的功能,我不会与您争论。
【解决方案4】:

这可能看起来有点难看,但通常可以为您提供所需的功能。

你可以有界面

public interface MyEnumInterface<T extends Enum<T>> {

    String getBusinessName();

    T getEnum();

}

实施

public enum OneOfMyEnums implements MyEnumInterface<OneOfMyEnums>{

    X, Y, Z;

    @Override
    public String getBusinessName() {
        return "[OneOfMyEnums]" + name();
    }

    @Override
    public OneOfMyEnums getEnum() {
        return this;
    }

}

实用类而不是你的父类

public class MyEnumUtils {

    public static <T extends Enum<T>> String doSomething(MyEnumInterface<T> e){
        e.getBusinessName(); // can use MyEnumInterface methods
        e.getEnum().name(); // can use Enum methods as well
        return null;
    }

}

【讨论】:

  • 这种方法很有趣,但通过让客户端依赖于实用程序类而不是enums(equals() 方法进入MyEnumUtils)来反转合同。
猜你喜欢
  • 1970-01-01
  • 2011-09-24
  • 1970-01-01
  • 2010-12-10
  • 2021-02-04
  • 1970-01-01
  • 2012-07-09
  • 1970-01-01
相关资源
最近更新 更多