While working on a project, I was presented with a task to design a set of classes that implement an interface defining a simple action. Usually these classes would do their job in particular sequence, all at once, but the possibility to call a method from only one of them was also a requirement.
Taking into account all the above and also considering that: - each class would have quite basic logic - extending another class was not required - it might be convenient to have all classes in a single file - editing source file when needed is not an issue
I came up with the following solution (actual class was not so contrived, but the example below is sufficient to give you some basic idea):
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
}
And code which calls this class might look like:
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);
}
}
During a code review my suggestion was mocked as the one that is "too hacky, exploites the fact that enums have class body and methods, and have a bad code smell in overall". While transforming it into a separate interface, a bunch of usual Java classes etc. is a matter of few minutes, I was not really quite satisfied with this explanation. Are there any guidelines saying that you should use enums exclusively in their basic form, similarly to other languages? What real drawbacks does this approach have? How about Joshua Bloch's suggestion to write singletons as enums - in this case such an enum will have to be a full-fledged class, right?