最近段时间,接到一个需求:开发一个聚合支付服务,对其余内部项目提供统一的接口来实现不一样支付平台的支付能力发起,好比支付宝,微信,银联等。为了处理类似的支付操做而各平台具体实现不一样的状况,要让各个平台接口能力能相互独立,并要方便扩展后续新增的支付平台,我引入了设计模式的策略模式来应对需求场景,借此深刻学习总结下策略模式,因而也就有了本文,但愿对学习策略模式的同窗有所帮助。html
平常工做开发中咱们总会遇到以下熟悉的代码片断:java
if(condition1){ // do something1 } else if (condition2){ // do something2 } else if (condition3){ // do something3 }
在每一个 if 条件下都有数十行甚至百行的业务处理,各自处理又是相互独立的而且目的一致,都汇聚在一个方法里。这样的写法不但让类变得臃肿冗长,而且不一样逻辑都在一个类中修改,维护和扩展起来都很费劲。那么又有什么办法能够优化这大段的代码呢,在实现功能的同时,让代码更加灵活和易维护。算法
要解决这个问题,本文的主角—策略模式 就登场了,做为设计模式中比较简单的行为型模式,其实不少框架中都见到它的身影,稍后咱们也会从各框架源码中识别策略模式的应用。使用策略模式能够帮助咱们将每一个处理逻辑封装成独立的类,客户端类须要进行哪一种处理逻辑就使用对应的类,调用其封装了业务处理细节的方法便可。这样一来,客户端类减小了业务处理逻辑的大量代码,让自身更加精简。当业务逻辑有所改动时,只要在对应的类中修改,而不影响其余的类;而且若是出现了新的业务逻辑只要新增类似的类进行实现,供客户端类调用便可。spring
接下来咱们就介绍下策略模式的定义和组成,以及它的基本形式。segmentfault
首先看下维基百科上策略模式的定义:设计模式
In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern) that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.
策略模式也叫政策模式,容许程序在运行时选择一个算法执行,一般存在一类算法实现提供外部选择执行,这里的算法,也能够叫作策略,至关于上节内容提到的具体处理逻辑。微信
再来看下 《设计模式:可复用面向对象软件的基础》一书中对策略模式的定义:app
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from the clients that use it.
再次对其定义解读:定义一类算法,各自独立封装实现,而且相互之间是可替换的。除此以外,由客户端类决定具体使用哪一个算法。框架
上述两个定义都提到了算法一词,它表示了完整的,不可再拆分的业务逻辑处理。一般用接口或者抽象类来表示一类算法的抽象,提供多种对该类算法的操做实现,以此组成一类独立且可替换的算法,也叫策略组。ide
了解完定义后,咱们再来看下策略模式通用类图:
类图中涉及三类角色:Context,Strategy 和 ConcreteStrategy
这三个角色的功能职责都十分明确,对应的源码实现也十分简单,如今咱们就来快速看下每一个角色对应的通用源码。
// 抽象的策略角色 public interface Strategy { void doSomething(); } // 具体策略角色 public class ConcreteStrategy implements Strategy { @Override public void doSomething() { System.out.println("ConcreteStrategy doSomething !"); } } // 上下文角色 public class Context { private final Strategy strategy; public Context(Strategy strategy) { this.strategy = strategy; } public void doAnything() { this.strategy.doSomething(); } }
有了策略模式的基本代码结构,在客户端类中使用十分简单,想要哪一个策略,就产生出它的具体策略对象放入上下文对象内,而后由上下文对象执行具体策略操做便可,具体代码以下:
public class Client { public static void main(String[] args) { Strategy strategy = new ConcreteStrategy(); Context context = new Context(strategy); context.doAnything(); // ConcreteStrategy doSomething ! } }
看清楚了策略模式的定义,角色组成以及通用的代码结构以后,咱们就来看下策略模式在通用框架里的应用,来加深对策略模式的认识。
在经常使用的Java 集合框架中,比较器 java.util.Comparator 的设计就采用了策略模式。Comparator 就是一个抽象的策略接口,只要一个类实现这个接口,自定 compare 方法,该类成为具体策略类,你能够在不少地址找到这个抽象策略接口的实现,官方在工具类 java.util.Comparators 里也提供 NaturalOrderComparator,NullComparator 两种具体策略类。而使用 Comparator 到的 java.util.Collections 类就是 Context 角色,将集合的比较功能封装成静态方法对外提供。
Spring 框架最先以 IoC 和 DI 两大特性著称,不须要开发者本身建立对象,而是经过 Spring IoC 容器识别而后实例化所需对象。在 Spring 中将执行建立对象实例的这个操做封装为一种算法,用接口类 org.springframework.beans.factory.support.InstantiationStrategy 进行声明,而具体策略类则有 org.springframework.beans.factory.support.SimpleInstantiationStrategy 和 org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy 两个,而且 CglibSubclassingInstantiationStrategy 是对 SimpleInstantiationStrategy 的继承扩展,也是 Spring 容器中真正使用到的策略类,具体应用的源码可参考 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory 类:
/** * Instantiate the given bean using its default constructor. * @param beanName the name of the bean * @param mbd the bean definition for the bean * @return a BeanWrapper for the new instance */ protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { //... beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); //... }
俗话说学以至用,接触了策略模式后咱们应该想一想怎么用在本身平常开发项目中呢,这里就简单经过一个实例来讲明下策略模式的使用方式。假设如今有个需求:须要对一个目录或者文件实现两种不一样格式的解压缩方式:zip压缩和gzip压缩,也后续可能新增其余的解压缩方式。
咱们首先将解压缩的算法抽象成抽象策略接口 CompressStrategy, 提供压缩方法 compress 和解压缩方法 uncompress,分别接受源文件路径和目的文件路径。
策略类在命名一般上以 Strategy 为后缀,来指明自身采用策略模式进行设计,以此简化与其余人沟通成本。
public interface CompressStrategy { public boolean compress(String source, String to); public boolean uncompress(String source, String to); }
再对抽象策略接口进行实现,分别提供zip 压缩算法和 gzip 压缩算法,代码以下:
public class ZipStrategy implements CompressStrategy { @Override public boolean compress(String source, String to) { System.out.println(source + " --> " + to + " ZIP压缩成功!"); return true; } @Override public boolean uncompress(String source, String to) { System.out.println(source + " --> " + to + " ZIP解压缩成功!"); return true; } } public class GzipStrategy implements CompressStrategy { @Override public boolean compress(String source, String to) { System.out.println(source + " --> " + to + " GZIP压缩成功!"); return true; } @Override public boolean uncompress(String source, String to) { System.out.println(source + " --> " + to + " GZIP解压缩成功!"); return true; } }
代码示例里的实现为了简化只是简单打印操做,具体实现能够参考 JDK API 进行操做。
接下来看下 Context 角色的代码实现:
public class CompressContext { private CompressStrategy compressStrategy; public CompressContext(CompressStrategy compressStrategy) { this.compressStrategy = compressStrategy; } public boolean compress(String source, String to) { return compressStrategy.compress(source, to); } public boolean uncompress(String source, String to) { return compressStrategy.uncompress(source, to); } }
十分简单,只是传入一个具体算法,而后执行,到这里标准的策略模式就编写完毕了。客户端类只是根据须要指定的具体压缩策略对象传给 CompressContext 对象便可。若是要新增一个压缩算法,也只需对 CompressStrategy 接口提供新的实现便可传给 CompressContext 对象使用。
public class Client { public static void main(String[] args) { CompressContext context; System.out.println("========执行算法========"); context = new CompressContext(new ZipStrategy()); context.compress("c:\\file", "d:\\file.zip"); context.uncompress("c:\\file.zip", "d:\\file"); System.out.println("========切换算法========"); context = new CompressContext(new GzipStrategy()); context.compress("c:\\file", "d:\\file.gzip"); context.uncompress("c:\\file.gzip", "d:\\file"); } }
上面的策略模式的应用示例是否是很简单,相似应用也有不少,好比要对接第三方支付,不一样的支付平台有不一样的支付API,这个API操做均可以抽象成策略接口,客户端发起特定平台的支付接口时,咱们只需调用具体的支付策略类执行,而且每一个支付策略类相互独立,可替换。
本节最后简单总结下策略模式的适用场景:
JDK 8 以后,利用Lambda能够提供策略模式更加精简的实现,若是策略接口是一个函数接口,那么不须要声明新的类来实现不一样策略,直接经过传递Lambda就可实现,而且更加简洁,具体使用方式参见下方代码:
/** * Context 对象 */ public class Validator { private final ValidationStrategy strategy; public Validator(ValidationStrategy v) { this.strategy = v; } public boolean validate(String s) { return strategy.execute(s); } } /** * 策略接口 */ @FunctionalInterface public interface ValidationStrategy { boolean execute(String s); } numericValidator = new Validator((String s) -> s.matches("[a-z]+")); b1 = numericValidator.validate("aaaa"); // true lowerCaseValidator = new Validator((String s) -> s.matches("\\d+")); b2 = lowerCaseValidator.validate("bbbb"); // false
结合 Lambda 的策略模式更适合用于处理简单算法操做的场景,若是算法实现复杂过于冗长复杂,仍是建议拆分红单个类进行实现。
策略模式使用起来虽然简单,但它的灵活性在许多项目都能见到其身影,在使用时也有须要注意的地方,下面咱们就来看下:
一个设计模式的引入必存在它合理的地方和不足,最后咱们再说说下策略模式的优缺点。
到这里,本文对策略模式的学习就此结束,固然关于策略模式的内容远不止这些,配合其余模式还有用法,感兴趣了的同窗能够参考文末提供的资料连接进一步深刻学习。也欢迎扫码关注微信公众号:「闻人的技术博客」,按期分享Java技术干货,共同进步。
本文由博客一文多发平台 OpenWrite 发布!