定义
策略模式定义了一个算法族,分别封装起来,使得它们之间可以互相变换。策略让算法的变化独立于使用它的客户。
优点
- 符合开闭原则,扩展性好、便于维护
- 提供了一种可替换继承的方法,可以避免多重条件选择语句
- 提供了一种复用机制,不同的类可以方便的复用策略类
缺点
- 在编写时必须知道所有策略类和其用途,并自行决定使用哪一个策略类
- 策略类如果很多的话,将会造成策略类膨胀
- 无法同时使用多个策略类
场景
在计算一个图形的周长时需要有一个计算周长的方法,如果图形父类直接定义一个计算周长的抽象方法给子类重写,那么子类将每次都要重写该方法,若图形改变其形状还需重新更改其代码。使用策略模式便可以将该方法定义在一个接口中,在父类中组合该接口,并且提供更改接口的方法,在父类中的计算周长的方法直接调用改接口的计算周长的方法,接口的实现类则是策略,其他的图形若使用的是相同的计算方式则可直接使用该接口的实现类。当图形的形状发生更改则只需要使用多态的特性更换该接口的实现类。
案例
描述
鸭子有很多的种类以及行为,不同的种类的行为也是不同。先设计一个鸭子父类所有的鸭子都将继承该父类,再将鸭子的行为设计成接口,比如飞行,叫声。鸭子父类将行为接口声明成引用变量,并且在鸭子父类中的飞行和叫声方法将其中的动作委托给行为类。在创建鸭子的子类时可以在构造器中初始化该鸭子的原始行为也就是创建一个行为的实现类的对象(该对象就是策略)。当鸭子的行为发生改变,比如一个玩具鸭原本不会飞经过改造之后可以借助外力飞起来。该鸭子原本是使用不会飞的策略现在要改成会飞的策略就只需要创建一个会飞的实现类的对象并将该鸭子的行为的引用属性设置成该对象(这里利用的是多态的特性)。在更改策略时并没有修改鸭子子类和行为的实现类的代码。
类图
代码
鸭子父类
/*duck父类*/
public abstract class Duck {
//飞行动作接口
FlyBehavior flyBehavior;
//叫声动作接口
QuackBehavior quackBehavior;
//委托给行为类
public void performFly(){
flyBehavior.fly();
}
public void performQuack(){
quackBehavior.quack();
}
public void setFlyBehavior(FlyBehavior fb){
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb){
quackBehavior = qb;
}
public void swim(){
System.out.println("鸭子在水里游!");
}
}
行为接口
/*duck飞行行为接口*/
public interface FlyBehavior {
public void fly();
}
/*叫声行为接口*/
public interface QuackBehavior {
public void quack();
}
飞行行为实现类
/*无法飞*/
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("飞不了!");
}
}
/*火箭带着飞*/
public class FlyRockerPowered implements FlyBehavior {
@Override
public void fly() {
System.out.println("火箭带我起飞!");
}
}
/*用翅膀飞*/
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("用翅膀飞!");
}
}
叫声行为实现类
/*叫不了*/
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
System.out.println("哑巴!叫不了!");
}
}
/*嘎嘎叫*/
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("在嘎嘎叫!");
}
}
鸭子子类
/*野鸭类*/
public class MallardDuck extends Duck{
public MallardDuck(){
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display(){
System.out.println("我可是真野鸭!");
}
}
/*橡皮鸭*/
public class RubberDuck extends Duck{
public RubberDuck (){
quackBehavior = new MuteQuack();
flyBehavior = new FlyNoWay();
}
public void display(){
System.out.println("我是橡皮鸭!");
}
}
测试类
public class MIniDuckSimulator {
public static void main(String[] args) {
Duck mallardDuck = new MallardDuck();
Duck rubberDuck = new RubberDuck();
System.out.println("野鸭");
mallardDuck.performQuack();
mallardDuck.swim();
System.out.println("橡皮鸭");
rubberDuck.performQuack();
rubberDuck.swim();
rubberDuck.performFly();
//更换策略
rubberDuck.setFlyBehavior(new FlyRockerPowered());
rubberDuck.performFly();
}
}
//运行结果
野鸭
在嘎嘎叫!
鸭子在水里游!
橡皮鸭
哑巴!叫不了!
鸭子在水里游!
飞不了!
火箭带我起飞!
理解
策略模式就是利用组合和多态来增强代码的可扩展性和可维护性,通过更换策略来做到不修改原本的代码,使用到了优先使用组合而不是继承的设计原则、针对接口编程而不是针对实现编程的设计原则,以及封装变化的设计原则。