一、简介
策略模式是行为型设计模式之一,它的原始定义是:定义一系列算法,将每一个算法封装起来,并使他们之间可以相互替换。策略模式让算法可以独立于客户端而变化。
其实现实生活中我们常常遇见实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开出旅行等。
在软件开发中,经常会遇到为了实现一个功能,我们可以通过多种算法达到目的,并且将这些算法集中到一个类中,每个算法对应一个方法,或者通过if...else等条件判断语句进行选择,但是这两种方法存在硬编码问题,违反了开闭原则,后期增加新的功能会变动原始代码,会导致代码维护困难。
比如网购,你可以选择工商银行、农业银行、建设银行等待,但是他们提供的算法都是一致的就是帮你付款。
二、原理
策略模式的结构还是比较简单的,由以下几类组成
①抽象策略类:抽象类,封装算法
②具体策略类:实现抽象策略类
③上下文类:策略模式的本质就是通过上下文类作为控制单元,对不同的类进行调度分配。
三、实现
3.1结构实现
抽象策略类
/**
* 策略抽象类:封装具体算法
*/
public interface Strategy {
void show();
}
具体策略类
/**
* 具体策略类
*/
public class ConcreateStrategyA implements Strategy{
@Override
public void show() {
System.out.println("执行策略A");
}
}
/**
* 具体策略类
*/
public class ConcreateStrategyB implements Strategy{
@Override
public void show() {
System.out.println("执行策略B");
}
}
上下文类
/**
* 上下文类:策略模式就是通过上下文类控制整个算法
*/
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void show(){
strategy.show();
}
}
测试类
public class Client {
public static void main(String[] args) {
Strategy concreateStrategyA = new ConcreateStrategyA();
Context context = new Context(concreateStrategyA);
context.show();
/**
* 执行策略A
*/
}
}
通过以上代码,我们实现了一个简单的策略模式框架,那么如何在实际场景只能怪应用呢,比较考验我们的抽象能力,一起来看看吧。
3.2应用实例
面试题:如何消除代码中的if-else
物流行业中,通常会涉及到EDI报文(XML格式文件)传输和回执接收,每发送一份EdI报文,后续都会收到相关的回执。
这里我们列举几种回执类型:MT1101,MT2101,MT4101,MT8104,系统在收到不同的回执报文后,会执行对应的业务逻辑处理,那么如何实现呢。
1)不使用设计模式
回执信息封装类
/**
* 回执信息类
*/
public class Receipt {
private String message; //回执消息
private String type; //回执类型MT1101,MT2101,MT4101,MT8104
public Receipt(String message, String type) {
this.message = message;
this.type = type;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
回执信息构建类
/**
* 回执信息类
*/
public class ReceiptBuilder {
public static List getReceipt() {
ArrayList<Object> list = new ArrayList<>();
list.add(new Receipt("MT1101回执信息", "MT1101"));
list.add(new Receipt("M4T101回执信息", "MT4101"));
list.add(new Receipt("M81101回执信息", "MT8101"));
return list;
}
}
客户端类
public class ReceiptClient {
public static void main(String[] args) {
List<Receipt> receipt = ReceiptBuilder.getReceipt();
for (Receipt receip : receipt) {
if ("MT1101".equals(receip.getType())){
System.out.println("处理MT1101");
}
if ("MT4101".equals(receip.getType())){
System.out.println("处理MT4101");
}
//......if...if
}
}
}
通过上述代码不难发现,我们有多个回执类型,每一个回执类型都要做一次if判断去单独执行对应的逻辑,要是只有个别几个回执类型还好,如果后续增加了新的回值类型呢,那么就要在代码中重新增加,严重的违反了java的开闭原则,使后续代码难维护,因此我们通过策略设计模式进行改造如下:
2)使用设计模式改造
回执信息抽象类
/**
* 回执信息抽象类
*/
public abstract class AbstractReceipt {
private String message; //回执消息
//回执信息对于的处理逻辑
abstract void processReceipt();
public AbstractReceipt(String message) {
this.message = message;
}
}
具体的回执信息实现
/**
* MT1101回执信息实现类
*/
class MT1101Receipt extends AbstractReceipt {
public MT1101Receipt(String message) {
super(message);
}
@Override
void processReceipt() {
System.out.println("Mt1101处理");
}
}
/**
* MT4101回执信息实现类
*/
class MT4101Receipt extends AbstractReceipt {
public MT4101Receipt(String message) {
super(message);
}
@Override
void processReceipt() {
System.out.println("Mt4101处理");
}
}
上下文类
/**
*上下文类
*/
class ReceiptContext{
private List<AbstractReceipt> receiptList=new ArrayList<>();
public void addReceipt(AbstractReceipt receipt){
receiptList.add(receipt);
}
/**
* 处理对应回执消息类型逻辑
*/
public void processReceipt(){
for (AbstractReceipt abstractReceipt : receiptList) {
abstractReceipt.processReceipt();
}
}
}
测试类
class Test{
public static void main(String[] args) {
ReceiptContext context = new ReceiptContext();
context.addReceipt(new MT1101Receipt("Mt1101"));
context.addReceipt(new MT4101Receipt("Mt1101"));
context.processReceipt();
/**
* Mt1101处理
* Mt4101处理
*/
}
}
通过上述代码我们发现,无论今后我们的回执类型如何增加,只需要增加对于的实现类就可以了,将代码彻底解耦出来,遵循了开闭原则。
四、总结
优点
①策略类之间可以自由切换,由于策略类都是实现同一个接口,所以使他们之间可以自由切换
②易于扩展,增加一个新的策略只需要新增一个类实现策略类即可,基本不改变原有代码,符合开闭原则
③避免使用多层if--else,充分体现面向对象设计思想。
缺点
①客户端必须知道所有的策略类,并自旋决定使用哪一个策略类
②策略模式将造成产生很多策略类,可以通过享元模式在一定程度上减少对象的数量。
使用场景
①一个系统需要动态的在几种算法中选择一种时,可将每个算法封装到策略类里面。
②一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类以代替这些条件语句。
③系统要求使用算法的客户不应该知道其操作数据时,可使用简单策略与隐藏与算法相关的数据结构。