策略模式(Strategy)
概述
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
应用场景
根据用户的需求处理数据时,需要对算法做出选择。这里的算法是指固定的算法(不再发生变化)。采用策略模式的好处是相比传统的switch或者if else更方便扩展,而且算法与用户端代码解耦。
之前研究设计模式的时候举的都是生活中的例子,后来觉得应该多举一些实际开发中用到的场景,这样才更便于理解,同时也可以在开发中选择适当场景使用。
把之前做的电商项目的支付流程提炼了一下,其中用户选择支付方式的情景就很适合使用策略模式。
各位客官,请往下看。
代码演示
首先看下电商常用的下单流程。
订单类
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;
}
//支付方法
//完美地解决了switch的过程,不需要在代码逻辑中写switch了
//更不需要写if else if
public PayState pay(Payment payment){
return payment.pay(this.uid,this.amount);
}
}
支付渠道接口
public interface Payment {
//uid 用户名 amount 交易金额
public PayState pay(String uid, double amount);
}
支付状态类
public class PayState {
private int code;//支付状态
private Object data;//交易详情
private String msg;//交易信息
public PayState(int code, String msg,Object data) {
this.code = code;
this.data = data;
this.msg = msg;
}
public String toString(){
return ("支付状态:[" + code + "]," + msg + ",交易详情:" + data);
}
}
支付宝交易渠道
public class AliPay implements Payment {
@Override
public PayState pay(String uid, double amount) {
System.out.println("欢迎使用支付宝");
System.out.println("查询账户余额,开始扣款");
return new PayState(200,"支付成功",amount);
}
}
微信交易渠道
public class WechatPay implements Payment {
@Override
public PayState pay(String uid, double amount) {
System.out.println("欢迎使用微信支付");
System.out.println("直接从微信红包扣款");
return new PayState(200,"支付成功",amount);
}
}
银联交易渠道
public class UnionPay implements Payment {
@Override
public PayState pay(String uid, double amount) {
System.out.println("欢迎使用银联卡支付");
System.out.println("查询账户余额,开始扣款");
return new PayState(200,"支付成功",amount);
}
}
测试类
public class PayStrategyTest {
public static void main(String[] args) {
//省略把商品添加到购物车,再从购物车下单
//直接从定单开始
Order order = new Order("1","20180311001000009",324.45);
//开始支付,选择微信支付、支付宝、银联卡、京东白条、财付通...
//每个渠道它支付的具体算法是不一样的
//基本算法固定的
//这个值是在支付的时候才决定用哪个值
System.out.println(order.pay(new AliPay()));
}
}
以上代码就可以实现由用户选择支付方式的功能,用户选择了支付宝,输出结果为:
但是这里又遇到了之前在工厂方法中遇到的问题,需要测试代码(用户端代码)自己来new对象new AliPay(),这个过程中就容易出现错误,还是让用户端来选择比较方便;还有就是假如我们需要修改支付类名称,用户端同样需要修改。
修改订单类:
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 PayState pay(PayType payType){
return payType.get().pay(this.uid,this.amount);
}
}
新增支付类型枚举类
public enum PayType {
ALI_PAY(new AliPay()),
WECHAT_PAY(new WechatPay()),
UNION_PAY(new UnionPay()),
JD_PAY(new JDPay());
private Payment payment;
PayType(Payment payment){
this.payment = payment;
}
public Payment get(){ return this.payment;}
}
修改测试类
public class PayStrategyTest {
public static void main(String[] args) {
Order order = new Order("1","20180311001000009",324.45);
System.out.println(order.pay(PayType.ALI_PAY));
}
}
这里再多说一句,为什么我们使用枚举类,而不是用常量来表示若干种支付方式呢?
因为在开发中发现当一组常量之间存在一定的关系时(这里的支付方式因为都很类似),使用枚举会使得程序易于扩展;且清晰。
当然,我们在实际开发中还有很多时候都使用到了策略模式:
之前做的爬虫项目,需要爬去百度,新浪,搜狗中的内容。然而每个网站的爬取规则是不同的,就可以将爬取规则写成策略模式。
在Spring中BeanFactory有很多子类,ListableBeanFactory等,而具体使用哪种工厂是由用户决定的,这里也是用到了策略模式。