我们在使用Java的枚举时往往会结合Switch来进行判断以实现不同值的处理,但是我们知道多用switch不是一种很好的代码风格,不利用维护和适应变化,因为这不符合开-闭原则。为此一种方法是用策略模式来重构原有的枚举实现,但是很多时候确实需要用枚举的方法来实现,如一周的天数等等,这也是JDK5.0重新引入枚举的原因。Joshua Bloch在Effective Java(第二版)里提出了一种枚举策略模式 很好的解决了这个问题。
要说明问题最好的办法就是结合例子,比如我们要定义一年的12个月,往往会想到枚举,假设我们要定义一个返回某年某个月的天数的方法,我们知道一个月一般是30,31天,其中2月比较特殊,闰年是28天,其他年份是29天,如果单纯用swtich的话显然不是一种很好的代码风格,这个例子还好,因为我们知道一年12月不会变,但有的枚举可能在今后添加新的类型,这样我们就不得不去修改原有的switch了,明显不符合开--闭原则,影响维护并且容易引入Bug。
这时我们可以利用嵌套枚举和接口结合的方法来实现开闭原则的思想。
在上面这段代码里Year枚举的ContainDays方法返回某年某月的天数,因为大月,小月,闰年和非闰年的2月返回的天数都是不一样的,也就是根据输入参数计算的策略可能不一样,这种情况下我们应抽象出接口来适应这种变化,但是要是直接在Year枚举里12月的类型实例一一实现该接口就会带来很多重复代码(代码有臭味了),因为像1,3,5等都是返回31天的,也就是计算方法一样,而如用嵌套枚举(相当于内部类),外层枚举把处理方法委托给了内部枚举,让内部枚举(代表每个月计算天数的不同类型)来具体实现Count接口(用来返回每月天数),则很好的消除了冗余代码,并且今后要是添加其他处理类型,只需再次实现Count接口,原有的代码无需改变,很好的实现了开闭原则。
要说明问题最好的办法就是结合例子,比如我们要定义一年的12个月,往往会想到枚举,假设我们要定义一个返回某年某个月的天数的方法,我们知道一个月一般是30,31天,其中2月比较特殊,闰年是28天,其他年份是29天,如果单纯用swtich的话显然不是一种很好的代码风格,这个例子还好,因为我们知道一年12月不会变,但有的枚举可能在今后添加新的类型,这样我们就不得不去修改原有的switch了,明显不符合开--闭原则,影响维护并且容易引入Bug。
这时我们可以利用嵌套枚举和接口结合的方法来实现开闭原则的思想。
public enum Year {
JAN(MonType.BIG), FEB(MonType.SPECIAL), MAR(MonType.BIG), APR(MonType.SMALL), MAY(
MonType.BIG), JUN(MonType.SMALL),//剩余月份略掉;
Year(MonType type) {
this.type = type;
}
private final MonType type;
public int ContainDays(int year) {
return type.countResult(year);
}
private enum MonType implements Count {
BIG {
public int countResult(int num) {
return 31;
}
},
SMALL {
public int countResult(int num) {
return 30;
}
},
SPECIAL {
public int countResult(int num) {
if ((num % 4 == 0 && num % 100 != 0) || (num % 400 == 0)) {
return 28;
} else {
return 29;
}
}
}
}
}
public interface Count {
public int countResult(int num);
}
在上面这段代码里Year枚举的ContainDays方法返回某年某月的天数,因为大月,小月,闰年和非闰年的2月返回的天数都是不一样的,也就是根据输入参数计算的策略可能不一样,这种情况下我们应抽象出接口来适应这种变化,但是要是直接在Year枚举里12月的类型实例一一实现该接口就会带来很多重复代码(代码有臭味了),因为像1,3,5等都是返回31天的,也就是计算方法一样,而如用嵌套枚举(相当于内部类),外层枚举把处理方法委托给了内部枚举,让内部枚举(代表每个月计算天数的不同类型)来具体实现Count接口(用来返回每月天数),则很好的消除了冗余代码,并且今后要是添加其他处理类型,只需再次实现Count接口,原有的代码无需改变,很好的实现了开闭原则。