策略模式(Strategy Pattern)
一、含义
抽象地来说:
策略模式定义了一个算法族,并对每个算法进行封装,使它们能够互换使用。
策略模式可以使算法独立于使用算法的客户端。
第一次看到这样定义难免会感到困惑,不急,让我们来通过一个案例来分析什么是策略模式。
二、案例分析
如果你要设计一个模仿各种汽车的类,那你可能会想到要先建立一个Car.java
抽象类,后面再通过继承它来创建宝马、奔驰等汽车。
abstract class Car {
public void run() {
}
}
class Baoma extends Car {
@Override
public void run() {
System.out.println("Baoma is running");
}
}
//...
现在我假设你已经用这种方式创造出了几百种汽车。
但是汽车不仅仅只有run()
这个方法,它还有很多种方法需要实现。
如果此时,让你给你创造的几百种汽车中添加添加能源这个功能,你会打算怎么做呢?
一般情况下,你可能会想到在基类Car.class
里加入fuel()
方法,然后每个子类再重写一下这个方法就可以了。
你也可能想到可以创造一个接口Fuelable.class
让每个子类继承它就好了。
上述两种方法都可以实现这个要求。
但是!!汽车能源方式最多只有几种,要么加油,要么充电等等等。
而你有几百种汽车,你真的要在每个汽车类中都重写一遍可能会重复的代码吗?
在这种情况下,策略模式应运而生~
对于添加能源的行为,我们可以给它创造一个接口FuelMethod.class
,但接下来的事不同于直接用具体的汽车类去实现它,而是创造出很多FuelMethod.class
的子类。
interface FuelMethod{
void fuel();
}
// 汽油
class QiYou implements FuelMethod{
@Override
public void fuel() {
System.out.println("这车加汽油的");
}
}
// 充电
class Dian implements FuelMethod {
@Override
public void fuel() {
System.out.println("这车充电的");
}
}
//...
同时,我们需要在基类Car.class
中添加一个FuelMethod
类属性,然后提供一个添加能源的方法。
而在你之前写的几百个汽车类中,我们不需要添加很多代码,只需要在构造器中实现FuelMethod
类就好,实现的具体类取决于车加的能源(这里的例子只有汽油或者电)。
abstract class Car {
// 加入“添加能源”类的属性
FuelMethod fuelMethod;
public Car() {
}
// 表示添加能源方式的方法
public void performFuel() {
if (fuelMethod != null) {
fuelMethod.fuel();
}
}
public void run() {
System.out.println("All cars can run");
}
}
class BMW extends Car {
public BMW() {
// 表示用汽油的
fuelMethod = new QiYou();
}
@Override
public void run() {
System.out.println("BMW is running");
}
}
class Tesla extends Car{
public Tesla() {
// 表示用电的
fuelMethod = new Dian();
}
@Override
public void run() {
System.out.println("Tesla is running");
}
}
//...
到了这里,我们已经完成了对汽车“添加能源”功能的添加,相比用每个类都去实现接口来说,节省了不少代码量吧?
写一个测试类让我们看看效果如何
public class MainTest {
public static void main(String[] args) {
Car BMW3 = new BMW();
BMW3.performFuel();
Car Tesla3 = new Tesla();
Tesla3.performFuel();
}
}
输出
这车加汽油的
这车充电的
如果想要动态地更换汽车能源方式,我们可以给Car.class
加个方法。这样所有的子类都可以用这个方法来更换添加能源的方式。
public void changeFuelMethod(FuelMethod method) {
this.fuelMethod = method;
}
测试类如下
public class MainTest {
public static void main(String[] args) {
Car Tesla3 = new Tesla();
Tesla3.performFuel();
Tesla3.changeFuelMethod(new QiYou());
Tesla3.performFuel();
}
}
输出
这车充电的
这车加汽油的
到此我们就完成了对汽车类的修改,是不是很酷?
综合来看,我们只添加了一个接口并写了几个实现类,然后我们在父类Car中写入这个接口类把它作为属性,最后在每个子类的构造器中实现这个FuelMethod对象。
三、总结
这就是我所要讲的策略模式。
在案例中,我们描述行为的方式发生了改变。我们不再将汽车添加燃料的事看作是个行为,而是把它看作是个算法族,不同的添加能源方式代表着不同的算法实现。
同时,我们是将汽车类和添加燃料类“组合”在了一起,注意这里是“组合”,与“继承”的方式是不同的,汽车的行为不是通过继承或实现接口来实现的,而是通过与适当的对象组合在一起实现的。
使用组合可以使我们的程序有更大的弹性,不仅在将算法族封装成类上,还是动态地改变行为,组合都将是一个很重要的技巧。
这个时候你再回顾一下开头的定义,是不是感到豁然开朗了?
策略模式定义了一个算法族,并对每个算法进行封装,使它们能够互换使用。
策略模式可以使算法独立于使用算法的客户端。