在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。
在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。
策略模式的定义
策略(Strategy
)模式的定义
:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式
,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
策略模式的结构
策略模式是准备一组算法,并将这组算法封装到一系列的策略类里面,作为一个抽象策略类的子类。策略模式的重心不是如何实现算法,而是如何组织这些算法,从而让程序结构更加灵活,具有更好的维护性和扩展性。
策略模式的主要角色如下:
抽象策略(Strategy
)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
具体策略(Concrete Strategy
)类:实现了抽象策略定义的接口,提供具体的算法实现。
环境(Context
)类:持有一个策略类的引用,最终给客户端调用。
策略模式的实现
/**
* 抽象策略类:支付方式
*/
public abstract class Payment {
public abstract String getName();
//通用逻辑放到抽象类里面实现
public MsgResult pay(String uid, double amount){
//余额是否足够
if(queryBalance(uid) < amount){
return new MsgResult(500,"支付失败","余额不足");
}
return new MsgResult(200,"支付成功","支付金额" + amount);
}
//查询余额的方法:供子类实现、、假设通过用户的id查余额
protected abstract double queryBalance(String uid);
}
/**
* 具体策略类:支付宝支付
*/
public class AliPay extends Payment {
public String getName() {return "支付宝";}
protected double queryBalance(String uid) {return 900;}
}
/**
* 具体策略类:京东白条支付
*/
public class JDPay extends Payment {
public String getName() {return "京东白条";}
protected double queryBalance(String uid) {return 500;}
}
/**
* 具体策略类:银联支付
*/
public class UnionPay extends Payment {
public String getName() {return "银联支付";}
protected double queryBalance(String uid) {return 120;}
}
/**
* 具体策略类:微信支付
*/
public class WechatPay extends Payment {
public String getName() { return "微信支付";}
protected double queryBalance(String uid) {return 263;}
}
/**
* 环境类:也就是策略模式的上下文,供用户调用
*/
public class Order {
private String uid;
private String orderId;
private double amount;
public Order(String uid, String orderId, double amount) {
this.uid = uid;
this.orderId = orderId;
this.amount = amount;
}
//默认支付方式:
public MsgResult pay(){
return pay(PayStrategy.DEFAULT_PAY);
}
//指定支付方式
public MsgResult pay(String payKey){
Payment payment = PayStrategy.get(payKey);
System.out.println("欢迎使用" + payment.getName());
System.out.println("本次交易金额为" + amount + ",开始扣款");
return payment.pay(uid,amount);
}
}
/**
* 工厂类:存放key和对应的策略类;一般策略模式和工厂模式搭配使用
*/
public class PayStrategy {
public static final String ALI_PAY = "AliPay";
public static final String JD_PAY = "JdPay";
public static final String WECHAT_PAY = "WechatPay";
public static final String UNION_PAY = "UnionPay";
public static final String DEFAULT_PAY = ALI_PAY;
private static Map<String,Payment> strategy = new HashMap<String,Payment>();
static {
strategy.put(ALI_PAY,new AliPay());
strategy.put(JD_PAY,new JDPay());
strategy.put(WECHAT_PAY,new WechatPay());
strategy.put(UNION_PAY,new UnionPay());
}
public static Payment get(String payKey){
if(!strategy.containsKey(payKey)){
return strategy.get(DEFAULT_PAY);
}
return strategy.get(payKey);
}
}
/**
* 返回的结果集类
*/
public class MsgResult {
private int code;
private Object data;
private String msg;
public MsgResult(int code, String msg, Object data) {
this.code = code;
this.data = data;
this.msg = msg;
}
@Override
public String toString() {
return "MsgResult{" +
"code=" + code +
", data=" + data +
", msg='" + msg + '\'' +
'}';
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
Order order = new Order("1","2020031401000323",324.5);
System.out.println(order.pay(PayStrategy.ALI_PAY));
}
}
程序运行结果如下:
欢迎使用支付宝
本次交易金额为324.5,开始扣款
MsgResult{code=200, data=支付金额324.5, msg='支付成功'}
举个反例,如果不使用策略模式代码会是什么样子的?
要写很多的if-else语句或者switch语句
if ("AliPay".equals(param)){
//支付宝的支付逻辑
}else if ("JdPay".equals(param)){
//京东的支付逻辑
}else if ("WechatPay".equals(param)){
//微信的支付逻辑
}else if ("UnionPay".equals(param)){
//银联的支付逻辑
}else if (...){
...
}
策略模式的应用场景
策略模式可以解决在有多种算法相似的情况下,使用if-else或switch所带来的复杂性和臃肿性。在日常业务中,策略模式适用于以下场景:
1.针对同一类型的问题,有多种处理方式,每一种都能独立解决问题。
2.算法需要自由切换场景
3.需要屏蔽算法规则的场景
策略模式的优缺点
策略模式的主要优点
如下:
1.策略模式符合开闭原则
2.避免使用多重条件转移语句,如if-else或switch
3.使用策略模式可以提高算法的保密性和安全性
其主要缺点
如下:
1.客户端必须知道所有策略算法的区别,以便适时选择恰当的算法类。
2.策略模式造成很多的策略类。
<<上一篇:模板方法模式
>>下一篇:命令模式