I am trying to make my code easier to extend in terms that a little change will not affect much other code.
I have an enum MyEnum
, which values might increase in future.
Then, there are classes that holds instance of it and has many behaviors affected by that enum's concrete value. In other words, there are many places where I switch over it's value.
public enum MyEnum
{
FIRST, SECOND, THIRD, FOURTH;
}
public class A
{
private MyEnum myEnum
public A(MyEnum myEnum)
{
this.myEnum = myEnum;
}
// as you will see, there is a lot of switching over its value
public void boo()
{
switch(myEnum)
{
case FIRST: // do smtng
case SECOND: // do smthing else
case THIRD: // do smthing else
case FOURTH: // do nice thing
}
}
public int goo()
{
switch(myEnum)
{
...
}
}
public AnotherObject foo()
{
switch(myEnum)
{
...
}
}
}
public class B
{
private MyEnum myEnum
public B(MyEnum myEnum)
{
this.myEnum = myEnum;
}
public double doo()
{
switch(myEnum)
{
...
}
}
public void soo()
{
switch(myEnum)
{
...
}
}
public boolean xoo()
{
switch(myEnum)
{
...
}
}
}
The thing here is that mostly I will need to add new case to all places where we switch over it's value => will need to do many changes to code when I add new enum value.
Did anyone else faced this problem? By now, I guess it is just downside of using enums this way.
Don't bind your code to the enum
bind your code to an interface. Then have your enum provide the standard implementations of the interface.
public interface RibbonColor {
public String getColor();
}
public enum DefaultRibbonColors implements RibbonColor {
FIRST() {
public String getColor() {
return "blue";
}
},
SECOND() {
public String getColor() {
return "red";
}
},
THIRD() {
public String getColor() {
return "white";
}
},
}
public class Awards {
private List<RibbonColor> ribbons;
public Awards(List<RibbonColor> ribbons) {
this.ribbons = ribbons;
}
public RibbonColor awardFor(int placeIndex) {
if (placeIndex < ribbons.length()) {
return ribbons.get(placeIndex).getColor();
}
return null;
}
}
Notice that now you can easily add in a new list of all the default Awards
by
Awards awards = new Awards(Arrays.asList(DefaultRibbonColors.values()));
while you could also create custom awards sets.
List ribbons = new ArrayList<RibbonColor>();
ribbons.addAll(DefaultRibbonColors.values());
ribbons.addAll(ExtendedRibbonColors.values());
ribbons.addAll(new BlackAndPinkPolkaDotRibbonColor());
Awards awards = new Awards(ribbons);
The key is to never make the code actually depend on the enum
because you can't modify an enum
without recompiling, and that triggers the need to search for switch
statements that lack default:
blocks or more explicit settings for the added value.
Objects are "code and data written together" while procedural code is "code and data managed separately" The switch
statement puts the logic "code" outside of the type "data" and is a programming mistake in 100% insanely object oriented design. That said, it is often useful, and people still structure programs in Java and other languages in ways that effectively separate code from data (object that hold all the data, and "object routines" that manipulate another object's data. This kind of separation of an object's data from its routines is an antipattern called anemic objects
.
Enums
are Objects
so don't be afraid to put methods in them! Give them interfaces where they should be replicable, and avoid switch statements because it's probably a good sign that the logic should be in the thing you are switching on (provided it is an Object).