策略模式(strategy pattern):定义了算法族,分别封闭起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户.
策略模式的结构
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。下面就以一个示意性的实现讲解策略模式实例的结构。
这个模式涉及到三个角色:
环境(Context)角色:持有一个Strategy的引用。
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
接下来用形象生动的案例让大家明白。现在公司里面已经开发了一款应用,主要是展示大自然中的狗的应用,在市场中得到了很大的反响,获得了很大的成功。
狗的抽象类:
/**
* 超类,所有的狗都要继承此类
* @author JUN
*
*/
public abstract class Dog {
//发出叫声
public void quack(){
System.out.println("旺旺旺");
}
//显示狗的外观
public abstract void display();
}
描述二哈类:
public class FoolishDog extends Dog {
public FoolishDog() {
super();
}
@Override
public void display() {
System.out.println("我是二哈,我老是做一些奇怪的表情!");
}
public void quack(){
System.out.println("啊嗚嗚嗚~~~");
}
}
描述柴犬类:
public class YellowDog extends Dog {
public YellowDog() {
super();
}
@Override
public void display() {
System.out.println("我是柴犬,我全身黃黃的");
}
}
但是为了应对激烈的竞争,公司需要变革才能保持已有的优势。这是就需要对产品新增一些新的功能。比如给狗增加飞行、上天的能力。
那么有什么方法可以给应用很灵活的增加这些功能呢?
继承:在父类中提供实现方法,子类通过继承获得父类中的飞行或者上天的行为。
虽然简单易用(粗暴丑陋),在已有应用中课快速添加飞行的能力。但是不具有灵活性,对未来变更支持差。
需要在子类中覆写飞行方法已提供新的飞行行为。这很容易造成错误。
抽象方法:在父类中提供抽象方法,强迫子类实现自己的飞行行为。
虽然足够灵活。但是很麻烦累人,每个子类都要实现一遍代码,即使相同的行为也不例外。代码重复,没有复用代码。
那么还有什么更好的办法吗?
当我们Java学习到一定程度就会明白:复合优于继承,多用组合,少用继承。
那么该如何使用策略模式呢?接下来就进行代码操作:
添加一个策略接口:
/**
* 策略接口
*
* @author JUN
*
*/
public interface FlyingStrategy {
void performFly();
}
在狗的抽象类中增加红框中的代码:
上述的柴犬和二哈都是自然界中真实的狗,接下来如果我们要给狗增加不能飞行、飞行和上天的行为,那么我们现在来添加一下具体的策略角色,具体策略角色需要实现我们的策略接口:
不能飞行的功能:
public class FlyNoWay implements FlyingStrategy {
@Override
public void performFly() {
System.out.println("我是走地狗!");
}
}
可以飞行的功能:
public class FlyWithWin implements FlyingStrategy {
@Override
public void performFly() {
System.out.println("我有翅膀,我會飛!");
}
}
可以上太空的功能:
public class FlyWithRocket implements FlyingStrategy {
@Override
public void performFly() {
System.out.println("我坐太空火箭!");
}
}
这时我们策略模式中的角色都有了:
接下来我们需要给柴犬和二哈添加一个相对应的行为,由于它们都是自然界中真实的狗都不能飞行,所以我们只需在构造方法中加一行代码就可以:
二哈类的变化:
柴犬类的变化:
好了,以上都准备完毕,接下来我们来感受一下策略模式的好处。公司下一阶段的项目将是开发会飞行的狗,我们只需这样添加一个类:
天狗类:
public class FlyDog extends Dog {
public FlyDog() {
super();
super.setFlyingStrategy(new FlyWithWin());
}
@Override
public void display() {
System.out.println("我是天狗,天狗望月!");
}
}
好了天狗类的产品已经完成,再下一阶段公司打算让狗上太空,那么我们依旧简单的这样添加一个类:
太空狗类:
public class SpaceDog extends Dog {
public SpaceDog() {
super();
super.setFlyingStrategy(new FlyWithRocket());
}
@Override
public void display() {
System.out.println("我是太空狗,太空狗登月!");
}
public void quack() {
System.out.println("我通过无线电与你通信");
}
}
接下来我们写一个狗的测试类:
public class DogTest {
public static void main(String[] args) {
System.out.println("测试开始");
System.out.println("*******");
Dog dog = null;
dog = new YellowDog(); //柴犬
// dog = new FoolishDog(); //二哈
// dog = new FlyDog(); //天狗
// dog = new SpaceDog(); //太空狗
dog.display();
dog.quack();
dog.fly();
System.out.println("***********");
System.out.println("测试完成");
}
}
逐一运行,柴犬和二哈的运行结果:
天狗和太空狗的运行结果:
现在有感觉到策略模式的好处了吧!这里建议我们多去代码实现一下,体验一下才能牢记。
最后我们总结一下:
策略模式的优点:
1、使用了组合,使架构更加灵活
2、富有弹性,可以较好的应对变化(开闭原则)
3、更好的代码复用(相对于继承)
4、消除大量的条件语句
缺点:
1、客户代码需要了解每个策略实现的细节
2、增加了对象的数目
策略模式的使用场景:
1、许多相关的类仅仅是行为差异
2、运行是选取不同的算法变体
3、通过条件语句在多个分支中选取一个