1.简述
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
2.角色
- Context(应用场景):需要使用ConcreteStrategy提供的算法。内部维护一个Strategy的实例。负责动态设置运行时Strategy具体的实现算法。负责跟Strategy之间的交互和数据传递。
- Strategy(抽象策略类):定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
- ConcreteStrategy(具体策略类):实现了Strategy定义的接口,提供具体的算法实现。
3.UML类图
4.通用代码
//抽象的策略角色
public interface Strategy{
//策略模式的算法接口
public void algorithm();
}
//具体策略角色1
public class ConcreStrategy1 implements Strategy{
public void algorithm() {
System.out.println("具体的算法实现策略1");
}
}
//具体策略角色2
public class ConcreStrategy2 implements Strategy{
public void algorithm() {
System.out.println("具体的算法实现策略2");
}
}
//封装角色
public class Context{
private Strategy strategy=null;
public Context(Strategy strategy) {
this.strategy = strategy;
}
//封装后的策略方法
public void doSomething() {
this.strategy.algorithm();
}
}
public class Client{
public static void main(String[] args) {
//声明一个具体的策略
Strategy strategy=new ConcreStrategy1();
Context context = new Context(strategy);
context.doSomething();
}
}
运行结果:
具体的算法实现策略1
5.举例说明
我们这里通过<<设计模式之禅>>这本书上的一个例子,来进一步了解策略模式。
场景如下:
刘备要到江东娶老婆了,走之前诸葛亮给赵云(伴郎)三个锦囊妙计,说是按天机拆开解决棘手问题,嘿,还别说,真是解决了大问题,搞到最后是周瑜陪了夫人又折兵呀,那咱们先看看这个场景是什么样子的。先说这个场景中的要素:三个锦囊,三个妙计,一个赵云,妙计是小亮同志给的,妙计是放置在锦囊里,俗称就是锦囊妙计嘛,那赵云就是一个干活的人,从锦囊中取出妙计,执行,然后获胜。我们来看看如何利用策略模式来实现这个过程。
妙计接口,代码如下:
//妙计接口
interface IStrategy{
//每个妙计都是一个可执行的算法
public void operation();
}
接口很简单,定义了一个方法operate,每个妙计都是可执行的,否则叫什么妙计。我们先来看第一个妙计——找乔国老开后门.。代码如下:
public class Backdoor implements IStrategy{
public void operation() {
System.out.println("找乔国老帮忙,让吴国太给孙权施加压力");
}
}
第二个妙计是找吴国太哭诉,企图给自己开绿灯,代码如下:
public class GivenGreenLight implements IStrategy{
public void operation() {
System.out.println("求吴国太开绿灯,放行");
}
}
第三个妙计是在逃跑的时候,让新娘子孙夫人断后,谁来砍谁,这是非常好的主意,代码如下:
public class BlockEnemy implements IStrategy{
public void operation() {
System.out.println("孙夫人断后,挡住追兵");
}
}
这样三个妙计都有了,还缺少一个锦囊,用来把三个妙计给装起来。另外还缺少使用锦囊妙计的人,这个人就是赵云了。代码如下:
//锦囊
public class Context{
private IStrategy stragety;
public Context(IStrategy strategy) {
this.stragety=strategy;
}
//使用妙计了,看我出招
public void operate() {
this.stragety.operation();
}
}
public class ZhaoYun {
//赵云出场了,他根据诸葛亮给他的交代,依次拆开妙计
public static void main(String[] args) {
Context context;
//刚到吴国的时候拆第一个
System.out.println("--刚到吴国的时候拆开第一个--");
context=new Context(new Backdoor()); //拿到妙计
context.operate(); //执行妙计1
System.out.println("--刘备乐不思蜀了,拆第二个了--");
context=new Context(new GivenGreenLight()); //拿到妙计
context.operate(); //执行妙计2
System.out.println("--孙权的小兵追来了,咋办?拆第三个--");
context=new Context(new BlockEnemy()); //拿到妙计
context.operate(); //执行妙计3
}
}
执行结果如下:
--刚到吴国的时候拆开第一个--
找乔国老帮忙,让吴国太给孙权施加压力
--刘备乐不思蜀了,拆第二个了--
求吴国太开绿灯,放行
--孙权的小兵追来了,咋办?拆第三个--
孙夫人断后,挡住追兵
相信看完这个例子你一定明白了策略模式是个怎样的设计模式了吧。我们接着看看策略模式有什么优点和缺点,以及它适用在什么场景下。
6.优点
- 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。
- 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。
- 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。
- 扩展性良好。这甚至都不用说是它的优点,因为它太明显了。在现有的系统中增加一个策略太容易了,只要实现接口就可以了。
7.缺点
- 策略模式造成很多的策略类。每个具体策略都会产生一个新类,复用可能性很小,类数量增多。
8.适用场景
- 多个类只有在算法或行为上稍有不同的场景。
- 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的 Strategy 类中以代替这些条件语句。
- 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
- 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。