当有一个算法(广义上的),它的功能是确定的,具体实现要视具体情况而定,比如计算不同国家税的算法,具体实现得看是哪个国家,那么一般想到的做法是用if-else或者switch-case来判断值,调用不同的计算函数:
class CountFuncs{
void func1() {
//具体实现
}
void func2() {
//具体实现
}
void func3() {
//具体实现
}
void func4() {
//具体实现
}
}
class GetTax{
void getTax(){
CountFuncs cFuncs=new CountFuncs();
String CountryName="";//此处应该从外部获得国家名字
if(CountryName.equals("China")) {
cFuncs.func1();
}else if(CountryName.equals("America")) {
cFuncs.func2();
}else if(CountryName.equals("England")) {
cFuncs.func3();
}else if(CountryName.equals("Japan")) {
cFuncs.func4();
}
}
}
我们看这个代码写得好不好,要放到时间轴上去看,要放到变化的角度去看,所以这个情况下很容易出现的一个变化是,要出现一个新的国家了怎么办,再增加一个func5,然后添加else if?这样又违反了开闭原则——类对扩展开放,对修改关闭。
那么我们就要将对方法的扩展改为对类扩展,将每一个方法编成一个类,而if-else则像TemplateMethod中一样,由参数传递,从子类向上转换为父类,来统一成父类的引用,但实际上是调用子类的具体实现函数:
abstract class Tax{
abstract void func();
}
class ChinaTax extends Tax{
@Override
void func() {
//具体实现
}
}
class AmericaTax extends Tax{
@Override
void func() {
//具体实现
}
}
class EnglandTax extends Tax{
@Override
void func() {
//具体实现
}
}
class JapanTax extends Tax{
@Override
void func() {
//具体实现
}
}
class GetTax{
void getTax(Tax country){
country.func();
}
}
这样写虽然类变的很多,但是却是符合开闭原则的。
有人会说,虽然这里通过函数参数传递将自子类转到父类,来避免if-else,但是在调用getTax方法的方法里,依然要使用if-else来从用户输入的字符串确定传递过来的是哪个子类吧。这个得学了工厂模式才能解决这个问题。
那么什么时候该使用if-else,什么时候不该使用if-else呢?
当这个if-else在未来可能会被扩展时,如上面这个例子,则要考虑使用策略模式。
当if-else在未来也不可能被扩展时,如一周七天,则不需要。一般自然规律是固定的,而客户需求是可能会被扩展的。
策略模式一般有一个抽象类,里面只有一个实现算法的函数,然后又若干个继承该抽象类的子类,一个类中也是只有一个实现算法的函数。
策略模式的重点是一个算法的多种实现,这个算法的实现方式在将来有可能还会被扩展,而模板方法模式的重点是,一个算法的整体流程是不变的,但是其中几个步骤会随情况不同而变化。