策略模式主要是抽象出不变的部分代码,然后定义算法簇将不同的算法分别封装起来,这就可以使得客户端不会受到算法改变的影响。这样对新增的需求就会由弹性支持,并不需要重构代码,只需要将该算法封装好就好了。其实官方的定义是策略模式将可变的部分从程序中抽象分离成算法接口,在该接口下分别封装一系列的算法实现,并且使得他们可以相互替换,从而导致客户端程序独立于算法的改变。
策略模式的实现
我们利用下面的例子来展示出策略模式的使用
首先我们需要编写一些实现类。
Duck.java:鸭子的总括,所有鸭子有的特性都在里面
package com.xjh.strategy;
public abstract class Duck {
/**
* 通用行为,由超类实现
*/
public void quack() {
System.out.print("嘎嘎嘎");
}
/**
* 特殊行为,声明为abstract,由子类实现
*/
public abstract void display();
}
MallardDuck.java和RedheadDuck.java:鸭子的具体实现
package com.xjh.strategy;
public class MallardDuck extends Duck {
public MallardDuck() {
super();
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("我的脖子是绿色的");
}
}
package com.xjh.strategy;
public class RedheadDuck extends Duck {
public RedheadDuck() {
super();
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("我的头是红色的");
}
}
假如我们想让鸭子增加一个飞行的能力有什么办法呢。肯定很多人都会想到在父类中定义飞行的方法,这样子类就具有了飞行的方法了。
继承
public void fly() {
System.out.print("fly");
}
的确这样可以实现我们想要的效果,并且简单易用,但是我们如果这样做了就不具有灵活性,对未来变更支持的能力变差,所以我们不会去使用这种方法,万一后面我们发现有鸭子不会飞怎么办。那我们就会想到前面我们display的实现方法。在父类中提供抽象方法,强迫子类实现自己的飞行方法。
抽象方法
public abstract void fly();
但是我们要想到我们的display方法是每种子类有不同的属性,但是对于飞行,我们要对所有的会飞行的子类都实现一遍相同的代码,这样是不是很麻烦呢。并且我们有相同代码而不进行代码复用,那以后这部分代码出现bug,是不是需要改很多的代码。
那就要提到组合的概念了:在类中增加一个私有域,引用另一个已有的类的实例,通过调用引用实例的方法从而获得新的功能,这种设计被称作组合。
组合
将飞行行为抽象为接口,下面就是策略模式中重要的抽象策略类
FlyingStragety.java
package com.xjh.strategy;
public interface FlyingStragety {
void performFly();
}
然后我们在父类中持有该接口,并由该接口代理飞行行为。
private FlyingStragety flyingStragety;。
public void setFlyingStragety(FlyingStragety flyingStragety) {
this.flyingStragety = flyingStragety;
}
public void fly() {
flyingStragety.performFly();
}
因为我们是调用接口的方法,并且我们接口是通过传参传进来的,所以更易于维护以及代码的复用。
之后我们就要定义接口的实现了,这就是具体策略类
FlyWithWin.java
package com.xjh.strategy;
public class FlyWithWin implements FlyingStragety {
@Override
public void performFly() {
// TODO Auto-generated method stub
System.out.println("翅膀飞行");
}
}
然后我们在MallardDuck.java和RedheadDuck.java将相应的接口实例传进去。
super.setFlyingStragety(new FlyWithWin());
如果遇到了没有飞行能力的子类怎么办,我们就需要重新编写一个FlyingStragety 接口的实现类。
FlyWithWin.java
package com.xjh.strategy;
public class FlyWithWin implements FlyingStragety {
@Override
public void performFly() {
// TODO Auto-generated method stub
System.out.println("不会飞行");
}
}
RubberDuck.java
package com.xjh.strategy;
public class RubberDuck extends Duck {
public RubberDuck() {
super();
super.setFlyingStragety(new FlyNoWay());
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("我全身发黄,嘴巴很红");
}
public void quack() {
System.out.print("我不会叫");
}
}
在这里我们就能看到策略模式的好处了,fly我们自需要new出一个新编写的接口实现类就好了,但是quack方法我们需要重写,如果我们忘记了重写,那橡胶鸭就会叫了,这就有了BUG了。
策略模式的优缺点
策略模式的优点:
- 使用了组合,使得架构更加灵活
- 富有弹性,可以较好的应对变化(开闭原则)
- 代码复用性(相对于继承)
- 消除大量的条件语句
策略模式的缺点:
- 客户代码需要了解每个策略实现的细节
- 增加了对象的数目
适用场景
- 当许多相关的类里面的内容仅仅是行为差异
- 当运行时选取不同的算法变体
- 当发现通过多个判断语句来选取不同行为的时候