1. 介绍
策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到客户端。从概念上看,这些算法完成的功能都是一样的,只不过是具体的实现不一样,使用策略模式可以让相同的方式调用所有的算法,减少了各种算法与使用算法类之间的耦合。
优点
① 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
② 简化了客户端代码,消除了多个if-else的判断;
③ 实现的选择 Strategy模式可以提供相同行为的不同实现。客户可以根据不同时间/空间权衡取舍要求从不同策略中进行选择。
缺点
① 会产生很多的策略类,每个策略都需要对应一个策略类;
② 提前需要知道有哪些策略,需要从中选择,指定最终执行的策略。
适用场景
① 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
② 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。
③ 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
④ 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
2. 应用案例
现在我们需要设计一款玩具汽车,具有基本的鸣笛和行驶的功能。
玩具汽车的超类如下:
public abstract class Car {
public Car(){
}
public abstract void display();
public void whistle(){
System.out.println("所有的汽车都会鸣笛");
}
public void run(){
System.out.println("所有的汽车都会行驶");
}
}
玩具汽车具体实现类如下:
public class ToyCar extends Car {
@Override
public void display() {
System.out.println("这是玩具车!");
}
}
public class Benz extends Car {
@Override
public void display() {
System.out.println("这是私家车!");
}
}
其他实现省略。
需求变动:玩具车需要具有飞行功能
简单的修改
在超类中新增fly方法,让所有的汽车都具备飞行的能力
public abstract class Car {
//其他方法省略
public void fly(){
System.out.println("所有的汽车都会飞");
}
}
产生的问题:绝大部分汽车本身不能飞,但是在Car类中添加了fly方法,却错误的赋予了它们飞行的能力,比如,奔驰车Benz,超类中新加的方法,会影响所有子类的行为。
简单的解决办法:最简单的解决办法就是利用方法的复写@Override,复写fly方法,根据不同汽车的特性去实现不同的fly。(缺陷:每新加一种汽车,可能就会去复写超类的方法,如,加入一只木制汽车,它可能既不会飞行,又不能发出声音,而且随着种类越多,那么复写的次数越多,重复的代码也就会越多,后期维护的时候可能就需要同时改很多个地方)
设计原则:应用中可能会产生变化之处,要把它们独立出来,不要和不需要变化的代码放在一处(不变化的代码一般是稳定的,加入变化的代码之后很可能就会破坏原来的稳定性)
独立变化的修改
变化的部分:飞行行为,鸣笛行为
//飞行行为
public interface FlyBehavior {
void fly();
}
//鸣笛行为
public interface WhistleBehavior {
void whistle();
}
Car超类修改
public abstract class Car {
private FlyBehavior flyBehavior;
private WhistleBehavior whistleBehavior;
public abstract void display();
public Duck(FlyBehavior flyBehavior, WhistleBehavior whistleBehavior) {
this.flyBehavior = flyBehavior;
this.whistleBehavior = whistleBehavior;
}
public void fly() {
flyBehavior.fly();
}
public void whistle() {
whistleBehavior.whistle();
}
public void run() {
System.out.println("所有汽车都会行驶");
}
// 加入set方法后就可以随时调用这两个方法来改变汽车的行为(策略模式的精髓,算法之间的相互替换)
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setWhistleBehavior(WhistleBehavior whistleBehavior) {
this.whistleBehavior = whistleBehavior;
}
}
Fly接口的实现类
- 不会飞行:
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("我无法飞行");
}
}
- 可以飞行
public class FlyWithPower implements FlyBehavior {
@Override
public void fly() {
System.out.println("我能够飞行");
}
}
鸣笛行为实现类
- 不会鸣笛
public class MuteWhistle implements WhistleBehavior {
@Override
public void whistle() {
System.out.println("无法鸣笛");
}
}
- 可以鸣笛
public class Whistle implements WhistleBehavior {
@Override
public void whistle() {
System.out.println("滴滴滴~");
}
}
具体的汽车类(玩具汽车)
public class ToyCar extends Car {
//在超类中有setFlyBehavior,setQuakBehavior,同样允许客户端自己选择
public ToyCar() {
super(new FlyWhithPower(), new Whistle());
}
@Override
public void display() {
System.out.println("这是一辆玩具汽车");
}
}
现在每辆汽车的鸣笛和飞行都可以自己选择灵活配置,不仅可以切换汽车,还可以切换汽车的行为,这在原来的设计上是办不到的,现在加一种新的汽车变得很简单了,而且不会对原来的设计造成影响。