我们在讲策略模式之前,先看看为什么要使用策略模式,用策略模式有什么好处
假定,我们面对着以下的需求:我们需要管理很多种类的汽车,需要用程序去描述他们
很自然的,我们会想到使用继承来复用代码,关系继承关系如下:
汽车能够跑,能够发出声音,roadster和bicycle通过继承Car,可以实现代码的复用
但是随着管理的车渐渐变多,我们发现,有的车有不同的行为方式,有不同的声音,甚至还有不同的方法,这个时候想用这种代码去管理会非常的吃力,要不断地去重写各个子类的方法,如果要增加新的方法,加在子类上不能重用代码,加在父类上,则要修正很多子类(有些子类没有此功能),这实在是太痛苦
所以我们需要把这些会改变的方法抽离出来,接下来我们来改进上面的方式
上图是我们改进后的类图,虚线代表着实现,实线代表着继承,我们把易改变的方法抽象出来,定义父接口RunBehavior与VocalBehavior,然后各种方法实例去实现这个接口
为了演示方便,我假定了一个超级汽车,可以天上飞,水中游,地上跑
下面来看代码
所有车的父类
public class Car {
//定义跑的行为
RunBehavior runBehavior;
//定义发声的行为
VocalBehavior vocalBehavior;
//把跑的行为委托给runBehavior去完成
public void run(){
runBehavior.run();
}
//把发声的行为委托给vocalBehavior
public void vocal(){
vocalBehavior.vocal();
}
}
这样做有什么好处呢?
可以让父类定义子类的方法,却不必关心子类的实现细节,降低耦合度
下面是SuperCar 和 Roadster的代码
public class SuperCar extends Car {
//在实例化的时候定义SuperCar的行为
public SuperCar(){
runBehavior=new InTheSky();
vocalBehavior=new Silence();
}
}
public class Roadster extends Car {
public Roadster(){
runBehavior=new OnTheGround();
vocalBehavior=new Voiced();
}
}
我们来写一个测试方法
public static void main(String[] args) {
Car roadster=new Roadster();
roadster.run();
roadster.vocal();
Car superCar=new SuperCar();
superCar.run();
superCar.vocal();
}
运行结果是:
这是在地上跑
这是有声的
这是在天上飞
这是无声的
我们来看看这样做有哪些好处?
假如,我们要重新为汽车加入一个方法,我们怎么做?
- 定义一个Behavior接口
- 写一个Behavior的实现子类
- 在父类加入一个Behavior属性
- 把具体的实现方法委托给这个Behavior去执行
在需要加入这个方法的子类里面的构造函数初始化这个Behavior
从这个看起来,好像也没啥优势,加入一个方法需要做的事也比较多
我们来看看,假如增加一个种类的汽车?
- 定义一个汽车类继承Car
- 构造函数直间给Behavior赋值就好了,如果没有想要的功能无非就是自己写一个功能实现
我们可以看到,这种方式大大提高了代码的复用,在之前,子类中的实现是没有办法复用的
你以为这就完了?SuperCar还没有开始表演呢,怎么可能就完了
我们来改一下代码
public class Car {
RunBehavior runBehavior;
VocalBehavior vocalBehavior;
public void run(){
runBehavior.run();
}
public void vocal(){
vocalBehavior.vocal();
}
public void setRunBehavior(RunBehavior runBehavior){
this.runBehavior=runBehavior;
}
}
我们在Car中加入了一个setRunBehavior的方法,用来运行时改变Car的行为
我们来测试一下看看
public static void main(String[] args) {
Car superCar=new SuperCar();
superCar.run();
superCar.setRunBehavior(new InTheWater());
superCar.run();
superCar.setRunBehavior(new OnTheGround());
superCar.run();
}
运行结果:
这是在天上飞
这是在水里游
这是在地上跑
通过这种方式我们可以实现运行时改变对象的行为
回过头来,我们看一看第二种方式做了什么?
- 把对象的方法抽象成一个接口
对象初始化的时候才定义方法的具体实现
我们看看原来对象和行为的关系是怎样的?
汽车的跑是在地上跑(已经定死了)现在的关系呢?
汽车有一个在地上跑的方式(可以换成别的方法)
我们把这一种行为叫做算法簇,单个的行为叫做一个算法,比方说RunBehavior,我们关注的是让汽车跑,但是他怎么跑则由你去指定算法去实现了,这就是策略模式
PS:源码地址