一、介绍
- 策略模式也叫政策模式,是一种比较简单的模式
- 定义一组算法,将每个算法都封装起来,并且使它们之间可以互换
二、原理类图
2.1 角色和职责
-
环境(Context)角色:该角色也叫上下文角色,起到承上启下的作用,屏蔽高层模块对策略、算法的直接访问,它持有一个Strategy类的引用。
-
抽象策略(Strategy)角色:该角色对策略、算法进行抽象,通常定义每个策略或算法必须具有的方法和属性。
-
具体策略(Concrete Strategy)角色:该角色实现抽象策略中的具体操作,含有具体的算法。
三、实例分析
下述代码用于实现任务描述 5.D.4,使用图书销售中不同的折扣算法演示策略模式。本实例中有3种折扣算法。
- 算法1:对有些图书没有折扣,即折扣算法中折扣值为0。
- 算法2:对有些图书的折扣是固定值为1元。
- 算法3:对有些图书的折扣是15%。
首先,对这3种算法使用DiscountStrategy类进行抽象,然后使用3个具体算法类继承这个类,其类图如图5-10所示。
- 抽象折扣算法DiscountStrategy类的代码如下所示。
【描述5.D.4】 DiscountStrategy.java
//抽象折扣算法类
public abstract class DiscountStrategy {
//书的价格
private double price = 0;
//书的数量
private int number = 0;
//构造函数
public DiscountStrategy(double price, int number){
this.price=price;
this.number =number;
}
//getter方法
public double getPrice() {
return price;
}
public int getNumber() {
return number;
}
//策略方法,计算折扣额
public abstract double calculateDiscount();
}
- 第一个具体折扣类NoDiscountStrategy继承DiscountStrategy类,实现没有折扣的算法,代码如下所示。
【描述5.D.4】 NoDiscountStrategy.java
//具体折扣,没有折扣算法
public class NoDiscountStrategy extends DiscountStrategy {
// 构造函数
public NoDiscountStrategy(double price, int number){
super(price,number);
}
//实现策略方法,0折扣额
public double calculateDiscount() {
return 0;
}
}
- 第二个具体折扣类FixDiscountStrategy也继承DiscountStrategy类,实现固定折扣的算法,代码如下所示。
【描述5.D.4】 FixDiscountStrategy.java
//具体折扣,固定折扣值为1的算法
public class FixDiscountStrategy extends DiscountStrategy {
// 构造函数
public FixDiscountStrategy(double price, int number){
super(price,number);
}
//实现策略方法,固定折扣额
public double calculateDiscount() {
return getNumber()*1;
}
}
- 第三个具体折扣类PercentageDiscountStrategy同样继承DiscountStrategy类,实现按照百分比折扣的算法,代码如下所示。
【描述5.D.4】 PercentageDiscountStrategy.java
//具体折扣,折扣百分比为15%的算法
public class PercentageDiscountStrategy extends DiscountStrategy {
// 构造函数
public PercentageDiscountStrategy(double price, int number){
super(price,number);
}
//实现策略方法,百分比为15%的折扣额
public double calculateDiscount() {
return getNumber()*getPrice()*0.15;
}
}
- 环境角色ContextClient的代码如下所示。
【描述5.D.4】 ContextClient.java
public class ContextClient {
private DiscountStrategy ds;
//构造函数
public ContextClient(DiscountStrategy ds){
this.ds=ds;
}
//调用策略模式,计算折扣额
public double contextCalDisc(){
return ds.calculateDiscount();
}
public static void main(String args[]) {
ContextClient context0=
new ContextClient(new NoDiscountStrategy(48.5,20));
System.out.println("0折扣:"+context0.contextCalDisc());
ContextClient contextFix=
new ContextClient(new FixDiscountStrategy(46,60));
System.out.println("固定折扣:"+contextFix.contextCalDisc());
ContextClient contextPer=
new ContextClient(new PercentageDiscountStrategy(38,40));
System.out.println("15%的折扣:"+contextPer.contextCalDisc());
}
}
- 上述代码分别实例化三种不同的折扣算法,计算图书的不同折扣额并输出,运行结果如下。
0折扣:0.0
固定折扣:60.0
15%的折扣:228.0
四、优缺点
4.1 优点
- **策略模式提供了管理相关的算法族的办法。**策略类的等级结构定义了一个算法或行为族,恰当地使用继承可以把公共的代码移到父类中,从而避免代码重复。
- 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为,如果不用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样算法或行为的使用者就和算法本身混在一起,从而不可能再独立演化。
- 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,这比使用继承的办法还要原始和落后。
4.2 缺点
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类,即策略模式只适用于客户端知道所有的算法或行为的情况。
- 策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保持到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。可以使用享元模式来减少对象的数量。
五、应用场景
- 多个类只是在算法或行为上稍有不同的场景。
- 算法需要自由切换的场景。
- 需要屏蔽算法规则的场景。