策略模式

策略模式(Strategy Pattern),将各种算法封装到具体的类中,作为一个抽象策略类的子类,使得它们可以互换。客户端可以自行决定使用哪种算法。

结构

策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。下面就以一个示意性的实现讲解策略模式实例的结构。

这个模式涉及到三个角色:
  ● 环境(Context)角色:持有一个Strategy的引用。
  ● 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  ● 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

认识策略模式

1、策略模式的重心
  策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。

2、算法的平等性
  策略模式一个很大的特点就是各个策略算法的平等性。对于一系列具体的策略算法,大家的地位是完全一样的,正因为这个平等性,才能实现算法之间可以相互替换。所有的策略算法在实现上也是相互独立的,相互之间是没有依赖的。
  所以可以这样描述这一系列策略算法:策略算法是相同行为的不同实现

3、运行时策略的唯一性
  运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个。

4、公有的行为
  经常见到的是,所有的具体策略类都有一些公有的行为。这时候,就应当把这些公有的行为放到共同的抽象策略角色Strategy类里面。当然这时候抽象策略角色必须要用Java抽象类实现,而不能使用接口。
  这其实也是典型的将代码向继承等级结构的上方集中的标准做法。

优点

  (1)策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
  (2)使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。
  (3)策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法(策略),并且可以灵活地增加新的算法(策略)。

缺点

  (1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
  (2)由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。

代码

环境角色类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Context {
//持有一个具体策略的对象
private Strategy strategy;

/**
* 构造函数,传入一个具体策略对象
* @param strategy 具体策略对象
*/
public Context(Strategy strategy){
this.strategy = strategy;
}

/**
* 策略方法
*/
public void contextInterface(){

strategy.strategyInterface();
}
}

抽象策略类

1
2
3
4
5
6
public interface Strategy {
/**
* 策略方法
*/
public void strategyInterface();
}

具体策略类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ConcreteStrategyA implements Strategy {

@Override
public void strategyInterface() {
//相关的业务
}

}

public class ConcreteStrategyB implements Strategy {

@Override
public void strategyInterface() {
//相关的业务
}

}

public class ConcreteStrategyC implements Strategy {

@Override
public void strategyInterface() {
//相关的业务
}

}

客户端调用代码

1
2
3
4
5
6
7
8
9
10
11
12
public class SimpleClient {

public static void main(String[] args) {
//选择并创建需要使用的策略对象
Strategy strategy = new ConcreteStrategyA();
//创建环境
Context context = new Context(strategy);
//执行
context.contextInterface();
}

}

从上面的示例可以看出,策略模式仅仅封装算法,提供新的算法插入到已有系统中,以及老算法从系统中“退休”的方法,策略模式并不决定在何时使用何种算法。在什么情况下使用什么算法是由客户端决定的。

和工厂模式的区别

在模式结构上,两者很相似。

差异:
1.用途不一样
工厂是创建型模式,它的作用就是创建对象;
策略是行为型模式,它的作用是让一个对象在许多行为中选择一种行为;

2.关注点不一样
一个关注对象创建
一个关注行为的封装

3.解决不同的问题
工厂模式是创建型的设计模式,它接受指令,创建出符合要求的实例;它主要解决的是资源的统一分发,将对象的创建完全独立出来,让对象的创建和具体的使用客户无关。主要应用在多数据库选择,类库文件加载等。
策略模式是为了解决的是策略的切换与扩展,更简洁的说是定义策略族,分别封装起来,让他们之间可以相互替换,策略模式让策略的变化独立于使用策略的客户。

4.工厂相当于黑盒子,策略相当于白盒子;