介绍
策略模式属于11种行为型模式之一
策略模式(strategy pattern)的原始定义是:定义一系列算法,将每一个算法封装起来,并使它们可以相互替换。策略模式让算法可以独立于使用它的客户端而变化
实际生活中常常遇到需要选择策略的场景,比如乘坐交通工具,可以选择飞机火车等
![](https://i-blog.csdnimg.cn/blog_migrate/ef5125c8a2618ae2ef8a41b263ec92f7.png)
在开发中,比如网购支付功能,支付时可以选择支付算法,可以是支付宝支付,也可以是xxx银行支付等待,这些提供的算法一致(付款),就是一种策略
所以,实际开发中,当实现某一个功能,有很多算法或者策略,就可以根据环境和条件的不同,选择不同的算法或策略来完成该功能
原理
在策略模式中可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里每一个封装算法的类都可以被称为一种策略,为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做算法的声明.而每种算法对应一个具体的策略类.
策略模式的主要角色如下:
抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
环境或上下文(Context)类:是使用算法的角色, 持有一个策略类的引用,最终给客户端调用。
![](https://i-blog.csdnimg.cn/blog_migrate/63c33864c3a9b5b9fe77d13944d50c15.png)
实现
策略模式的本质是通过Context类来作为中心控制单元,对不同的策略进行调度分配。
/**
* 抽象策略类
**/
public interface Strategy {
void algorithm();
}
/**
* 策略类A
**/
public class ConcreteStrategyA implements Strategy {
@Override
public void algorithm() {
System.out.println("执行策略A");
}
}
/**
* 策略类B
**/
public class ConcreteStrategyB implements Strategy {
@Override
public void algorithm() {
System.out.println("执行策略B");
}
}
/**
* 环境类
**/
public class Context {
//维持一个对抽象策略类的引用
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
//调用策略类中的算法
public void algorithm(){
strategy.algorithm();
}
}
public class Client {
public static void main(String[] args) {
Strategy strategyA = new ConcreteStrategyA();
Context context = new Context(strategyA); //可以在运行时指定类型,通过配置文件+反射机制实现
context.algorithm();
}
}
示例
描述:在物流系统中,需要通过不同的回执进行不同的处理,举例如MT1101回执类型,系统收到回执,根据回执类型执行相应逻辑处理
不使用策略模式时
回执类
/**
* 回执信息
**/
public class Receipt {
private String message; //回执信息
private String type; //回执类型(MT1101、MT2101、MT4101、MT8104)
public Receipt() {
}
public Receipt(String message, String type) {
this.message = message;
this.type = type;
}
// ...get set 方法
}
客户端
public class Client {
public static void main(String[] args) {
//模拟收到回执信息
List<Receipt> receiptList = new ArrayList<>();
receiptList.add(new Receipt("MT1101回执","MT1011"));
receiptList.add(new Receipt("MT2101回执","MT2101"));
receiptList.add(new Receipt("MT4101回执","MT4101"));
receiptList.add(new Receipt("MT8104回执","MT8104"));
//循环判断
for (Receipt receipt : receiptList) {
if("MT1011".equals(receipt.getType())){
System.out.println("接收到MT1011回执!");
System.out.println("解析回执内容");
System.out.println("执行业务逻辑A"+"\n");
}else if("MT2101".equals(receipt.getType())){
System.out.println("接收到MT2101回执!");
System.out.println("解析回执内容");
System.out.println("执行业务逻辑B"+"\n");
}else if("MT4101".equals(receipt.getType())) {
System.out.println("接收到MT4101回执!");
System.out.println("解析回执内容");
System.out.println("执行业务逻辑C"+"\n");
}else if("MT8104".equals(receipt.getType())) {
System.out.println("接收到MT8104回执!");
System.out.println("解析回执内容");
System.out.println("执行业务逻辑D");
}
//......
}
}
}
使用策略模式优化
策略接口
/**
* 回执处理策略接口
**/
public interface ReceiptHandleStrategy {
//回执处理
//param receipt: 接收到的回执信息
void handleReceipt(Receipt receipt);
}
实际策略类
public class Mt1011ReceiptHandleStrategy implements ReceiptHandleStrategy {
@Override
public void handleReceipt(Receipt receipt) {
System.out.println("解析报文MT1011: " + receipt.getMessage());
}
}
public class Mt2101ReceiptHandleStrategy implements ReceiptHandleStrategy {
@Override
public void handleReceipt(Receipt receipt) {
System.out.println("解析报文MT2101: " + receipt.getMessage());
}
}
......
策略上下文
/**
* 上下文类,持有策略接口
**/
public class ReceiptStrategyContext {
private ReceiptHandleStrategy receiptHandleStrategy;
public void setReceiptHandleStrategy(ReceiptHandleStrategy receiptHandleStrategy) {
this.receiptHandleStrategy = receiptHandleStrategy;
}
//调用策略类中的方法
public void handleReceipt(Receipt receipt){
if(receipt != null){
receiptHandleStrategy.handleReceipt(receipt);
}
}
}
策略工厂
public class ReceiptHandleStrategyFactory {
public ReceiptHandleStrategyFactory() {
}
//使用Map集合存储策略信息,彻底消除if...else
private static Map<String,ReceiptHandleStrategy> strategyMap;
//初始化具体策略,保存到map集合
public static void init(){
strategyMap = new HashMap<>();
strategyMap.put("MT1011",new Mt1011ReceiptHandleStrategy());
strategyMap.put("MT2101",new Mt2101ReceiptHandleStrategy());
}
//根据回执类型获取对应策略类对象
public static ReceiptHandleStrategy getReceiptHandleStrategy(String receiptType){
return strategyMap.get(receiptType);
}
}
客户端
public class Client {
public static void main(String[] args) {
//模拟收到回执信息
List<Receipt> receiptList = new ArrayList<>();
receiptList.add(new Receipt("MT1101回执","MT1011"));
receiptList.add(new Receipt("MT2101回执","MT2101"));
receiptList.add(new Receipt("MT4101回执","MT4101"));
receiptList.add(new Receipt("MT8104回执","MT8104"));
//策略上下文
ReceiptStrategyContext context = new ReceiptStrategyContext();
//策略模式将策略的 定义、创建、使用这三部分进行了解耦
for (Receipt receipt : receiptList) {
//获取置策略
ReceiptHandleStrategyFactory.init();
ReceiptHandleStrategy strategy = ReceiptHandleStrategyFactory.getReceiptHandleStrategy(receipt.getType());
//设置策略
context.setReceiptHandleStrategy(strategy);
//执行策略
context.handleReceipt(receipt);
}
}
}
当需要新添加一种回执时,只需添加对应回执策略类,在策略工厂中list添加;如果需要符合开闭原则,则需修改策略工厂中获取回执类方式,可采用反射方式,获取指定包路径下所有实现ReceiptHandleStrategy接口的实际策略类
总结
优点
策略类之间可以自由切换
由于策略类都实现同一个接口,所以使它们之间可以自由切换。
易于扩展
增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合"开闭原则"
避免使用多重条件选择语句(if else),充分体现面向对象设计思想。
缺点
客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。