枚举而不是Java中的常用类

Ale*_*lov 5 java enums design-patterns

在处理项目时,我遇到了一项任务,即设计一组实现定义简单操作的接口的类.通常这些类会按特定顺序完成它们的工作,但是同时只能从其中一个调用方法的可能性也是必需的.

考虑到以上所有因素并考虑到: - 每个类都有相当基本的逻辑 - 不需要扩展另一个类 - 将所有类放在一个文件中可能很方便 - 在需要时编辑源文件不是问题

我提出了以下解决方案(实际课程不是那么做作,但下面的例子足以给你一些基本的想法):

public enum Bestiary {
DOG(1) {
    @Override
    void makeNoise(Loudspeaker ls) {
        ls.shoutOutLoud("I am alpha dog");
    }
},

CAT(2) {
    @Override
    void makeNoise(Loudspeaker ls) {
        ls.shoutOutLoud("I am beta cat");
    }
},

RAT(3) {
    List<String> foods = new ArrayList<>();
    {
        foods.add("gods");
        foods.add("dogs");
        foods.add("cats");
        foods.add("other rats");
    }

    @Override
    void makeNoise(Loudspeaker ls) {
        StringBuilder cry = new StringBuilder("I am THE rat; usually I eat ");
        for (int i = 0; i < foods.size(); i++) {
            cry.append(foods.get(i));
            if (i != (foods.size() - 1)) {
                cry.append(", ");
            }
        }
        ls.shoutOutLoud(cry.toString());
    }
},

THE_THING(4) {
    String name = "r2d2";

    @Override
    void makeNoise(Loudspeaker ls) {
        ls.shoutOutLoud(calculateHash(name));

    }

    private String calculateHash(String smth) {
        return String.valueOf(smth.hashCode());
    }
};

private int id;

public int getId() {
    return id;
}

Bestiary(int id) {
    this.id = id;
}

abstract void makeNoise(Loudspeaker ls); // all enum elements will need to implement this - kind of like implementing an interface (which was also an option); note that we pass some arbitrary object and call methods on it
}
Run Code Online (Sandbox Code Playgroud)

调用此类的代码可能如下所示:

public final class Loudspeaker {
private static Loudspeaker loudspeaker = new Loudspeaker();

public static void shoutOutLoud(String cry) {
    System.out.println(cry);
}

static class Noizemakers {
    public static void makeSomeNoise() {
        for (Bestiary creature: Bestiary.values()) {
            System.out.println(creature + " with id " + creature.getId() +  " says: ");
            creature.makeNoise(loudspeaker);
        }
    }
}

public static void main(String[] args) {
    Noizemakers.makeSomeNoise();
    Bestiary.CAT.makeNoise(loudspeaker);
}
}
Run Code Online (Sandbox Code Playgroud)

在代码审查期间,我的建议被嘲笑为"过于hacky,利用枚举具有类体和方法的事实,并且整体上有一个糟糕的代码味道".在将它转换为单独的接口时,一堆常见的Java类等只需几分钟,我对这个解释并不十分满意.是否有任何指导方针说您应该以其基本形式使用枚举,与其他语言类似?这种方法有什么真正的缺点?约书亚布洛赫建议把单身人士写成名词 - 在这种情况下,这样的词汇必须是一个完整的课程,对吧?

sha*_*kan 3

人们可以在具有浅层类层次结构的任何地方使用它enum,并在可扩展性(类较多,枚举较少)和简洁性(如果功能简单,枚举可能更清晰)之间进行权衡。始终这样做并不是正确的做法,但偶尔这样做当然是可以的,只需注意其中的差异即可,我在下面列出了其中的一些差异。

在我看来,您所处理的情况似乎正是语言设计者通过允许枚举拥有方法来支持的那种情况。在我看来,您丝毫没有颠覆该语言功能的意图。

作为我工作中的一个例子,我经常使用枚举和方法作为实现各种无状态策略的方式,但也将它们用于其他用途,包括作为Class.

对您的具体问题的回答:

这种方法有什么真正的缺点?

与接口+具体类方法相比:

  • 无法从该值外部调用在特定枚举值中定义的方法。例如,如果您为 RAT 定义了一种名为 的方法squeak(),则没有人可以调用它。
  • 没有可变状态,因为每个枚举值实际上都是单例。
  • 如果类型数量急剧增加,或者每种类型的代码增加,您的枚举类文件可能会变得过长。
  • 不能对枚举值进行子类化,它们实际上是final
  • 毫无疑问还有其他一些...

是否有任何指导原则表明您应该仅以基本形式使用枚举,与其他语言类似?

我从未见过。

Joshua Bloch 建议将单例写为枚举怎么样?在这种情况下,这样的枚举必须是一个成熟的类,对吧?

按照提问者的逻辑,是的。所以问题就变成了你愿意听他们的还是乔什·布洛赫的。