策略模式(Strategy Pattern)
在策略模式中,一个行为可以在系统运行时动态修改,这种类型设计模式是行为设计模式,策略模式中,会有代表各种策略的对象和执行策略的context上下文对象,策略对象改变context执行的算法。
举例:
支付系统中,很多支付策略,比如 支付宝、微信、银联,在支付时需要选择其中一种支付策略进行支付业务
简要介绍
1、用途:定义一系列策略,他们实现自己的算法,使用策略模式实现在运行时动态替换各个策略
2、解决难点:在很多类似算法的实现中,使用 if …else 会造成难以维护和扩展
3、使用场景:在一个系统中有很多类,它们之间唯一的区别只在于行为的不同,而且需要系统动态选择这些行为中的哪一种,如果不使用适当的设计模式,就会产生多重条件选择语句。
设计
需求说明:系统需要添加支付功能模块,且支付渠道提供支付宝,微信,银联三种,通过各自支付方式进行支付。
上述的三种支付渠道,当用户选择一种后,到达后台进行业务处理,会有三个条件选择,于是伪代码如下:
if(支付宝){
支付宝.pay();
//...
}else if(微信){
微信.pay();
//...
}else if(银联){
银联.pay();
//...
}else{
未知支付方式...
}
感觉没啥问题。
但是假如项目上线了,公司业务壮大,想要引入第四种支付渠道,比如是一个第三方渠道商,就叫支付宝二号,现在需要拓展时,就需要找到这个项目的源代码,更改IF语句后重新编译项目,打包运行。这样就会多出来一堆的维护时间,而且如果期间改错代码,也会浪费很多时间。
那么,如何使用策略模式实现这种运行时动态选择行为策略,而且后期容易拓展的支付系统呢?
首先设计类图,如下图
实现
1、抽象类,既然需要动态更改行为,必须使用多态
public interface IPayStrategy{
public void pay();//支付
}
2、各个行为对象
微信支付
public class WexinPayStrategy implements IPayStrategy {
@Override
public void pay() {
// 业务逻辑代码
System.out.println("微信支付.");
}
}
支付宝支付
public class AliPayStrategy implements IPayStrategy{
@Override
public void pay() {
// 业务逻辑代码
System.out.println("支付宝支付.");
}
}
银联支付
public class EBankPayStrategy implements IPayStrategy{
@Override
public void pay() {
// 业务逻辑代码
System.out.println("银联支付.");
}
负责调度,执行入口的上下文 context
public class PayStrategyContext {
// 支付策略的接口
private IPayStrategy payStrategy;
public PayStrategyContext() {
}
/**
* 执行支付方法
*/
public void excutePay() {
if (null == payStrategy) {
throw new RuntimeException("支付策略未配置");
}
payStrategy.pay();
}
public IPayStrategy getPayStrategy() {
return payStrategy;
}
public void setPayStrategy(IPayStrategy payStrategy) {
this.payStrategy = payStrategy;
}
}
测试类
public class MainTest {
public static void main(String[] args) {
//执行上下文
PayStrategyContext payStrategyContext = new PayStrategyContext();
IPayStrategy payStrategy = null;
//1.支付宝支付
payStrategy = new AliPayStrategy();
payStrategyContext.setPayStrategy(payStrategy);
payStrategyContext.excutePay();
//2.微信支付
payStrategy = new WexinPayStrategy();
payStrategyContext.setPayStrategy(payStrategy);
payStrategyContext.excutePay();
//3.银联支付
payStrategy = new EBankPayStrategy();
payStrategyContext.setPayStrategy(payStrategy);
payStrategyContext.excutePay();
}
}
执行结果:
支付宝支付.
微信支付.
银联支付.
此外,以上案例就是整个策略模式的架构,其实通过观察测试类可以发现,策略模式在编写执行逻辑时,只要改动的代码就是在创建对象这里:
那么,我们可以想到动态创建对象的方法之一:反射。如果这样,那就可以往数据库中存储这些策略的标识,然后新建一个工具类负责根据这些标识动态创建各个策略的不同对象。当然,一般来说,在系统中,这些策略应该时单例存在的,所以应该要把上述例子中的各个策略对象变为单例对象,已实现内存优化。
以下是优化后的版本类图:
这三种支付行为,其实在系统中可以使用单例,以此节省创建对象时间和内存空间。所以作出以下修改:
/**
* 这里就拿支付宝支付这种支付方式作为例子,其他支付方式的类跟这类似
* 特别说明:因我们的策略想要让系统更加智能,选择对应策略。所以都需要时单例,并且提供 getInstance()
* 方法,以获取这个单例对象;
*/
public class AliPayStrategy implements IPayStrategy{
private static AliPayStrategy aliPayStrategy;
@Override
public void pay() {
System.out.println("支付宝支付.");
}
public static AliPayStrategy getInstance(){
if(null == aliPayStrategy){
aliPayStrategy = new AliPayStrategy();
}
return aliPayStrategy;
}
}
以后的支付方式,如需拓展,就添加一个单例类,提供 getInstance()方法,实现 pay() 支付方法,并且将这个类放在 特定的包路径下,这样,我们就可以将支付方式字符串写入配置文件或者存储到数据库中,再利用工具类给context上下文提供这种支付方式的单例对象, context 利用这个单例对象,调用 pay() 方法,以此实现真正支付。
下面是这个工具类:
public class PayStrategyUtils {
public static IPayStrategy getPayStrategy(String payType){
try {
// 这个路径需要根据你来定义,属于一种规则,这样一来,将所有策略都写在该包下即可
String path = "strategyDesignModel."+payType.concat("PayStrategy");
// 反射,类加载器
Class<?> clazz = Class.forName(path);
// 调用instance()方法获取单例对象
IPayStrategy instance = (IPayStrategy) clazz.getDeclaredMethod("getInstance").invoke(null, null);
return instance;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Load [" + payType.concat("Strategy") + "] Error :", e);
}
}
}
测试类:
public class MainTest2 {
public static void main(String[] args) {
PayStrategyContext payStrategyContext = new PayStrategyContext();
IPayStrategy payStrategy = null;
payStrategy = PayStrategyUtils.getPayStrategy("Ali");
payStrategyContext.setPayStrategy(payStrategy);
payStrategyContext.executePay();
}
}
执行结果:
支付宝支付.
到这里就会发现,我们的策略就只需要在业务层传入一个参数即可,这个参数可以存储在数据库,也可以是写在文件中。这样如果后期需要添加一种策略,只需要添加一个策略的实现(就是类似上面的微信 WexinPayStrategy,支付宝 AliPayStrategy这些),再添加一个策略参数到配置文件或数据库即可(就是 Ali,Wexin,EBank)。
总结
文章主要介绍策略模式的应用场景,和一些基本架构。我看过一些资料,可能在类图中并没有工具类,但是为了简便,所以加了这一点。所有的设计模式,使用到对的场景就是最好的,在项目中会遇到类似业务,可以考虑使用,以减少后期的时间成本,经历成本。