1、策略模式概念理解
官方定义:针对一组算法或行为,将每一个算法或行为封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
为了方便理解概念我们先来模拟一个需求场景: 现在假设公司要你开发一个支付抵扣功能。背景:现在有支付宝、微信、银联等支付渠道。需求:用在支付时可以用一种叫代金券的虚拟券抵扣支付金额,不排除今后会新增其他抵扣方式。
接下来我们来看看下面几种不好的设计:
abstract class 支付基类{ 抵扣方法(){} abstract 支付方法(); } class 支付宝类 extends 支付基类{ 支付方法(){ 抵扣金额=抵扣方法() .... } } class 微信类 extends 支付基类{ 支付方法(){ 抵扣金额=抵扣方法() .... } } class 银联类 extends 支付基类{ 支付方法(){ 抵扣金额=抵扣方法() .... } }
....
上面设计中,将抵扣方法写在基类中,这样各个子类都可以调用。
过了一段时间,需求有所改变,要求某些支付渠道(例如:银联渠道)不能抵扣,此时我们就要改代码,在银联类里覆盖抵扣方法。如果类似情况较多那么每个支付渠道都需要检查是否需要重写抵扣方法。
再比如,新增了一种支付渠道叫积分抵扣(类似于淘宝),某些支付渠道只能用积分抵扣,这样也需要在支付类里重写抵扣方法,但这些抵扣方法不能被重用。
所以这样的设计至少不是一种优雅的设计。
PS:使用继承来实现代码复用不是一种很好的方法。
2、策略模式的核心思想
根据上面的例子我们可以看出抵扣的这个行为,可能有很多种方式,并且下个的抵扣方式是什么样,我们无法预估。因为我们无法预估需求。
对于像这样的场景,一个类有一组算法或者行为 (上面的例子中指的就是“抵扣”,抵扣方式有很多种所以说他有“一组算法或行为”),这样的需求该怎么设计呢?
“将每一个算法或行为封装到具有共同接口的独立的类中”,意思是把抵扣的每个行为看成一个个类,具体抵扣规则写在类方法里,这些类实现同一个接口。具体看下面UML:
在上面的设计中,我们把抵扣的行为不放在支付基类或子类里,而是将他独立出来,基类里包含设置行为的方法,这样可以动态的改变行为。这样的设计支付类将不用知道抵扣细节,从而解耦。
5、代码示例
5.1、代码示例(Java)
客户端类 Client.java
package strategy; public class Client { public static void main(String[] args) { PayBase zfb = new Zhifubao(); zfb.setDikouBehavior(new DaiJinQuan()); zfb.payAction();
zfb.setDikouBehavior(new JIfen());
zfb.payAction();
} }
支付基类 PayBase.java
package strategy; public abstract class PayBase { DikouBehavior dikou; public void setDikouBehavior(DikouBehavior dk){ dikou = dk; } protected abstract void payAction(); }
支付宝类 Zhifubao.java
package strategy; class Zhifubao extends PayBase{ public void payAction(){ dikou.dikouAction(); System.out.println("支付宝支付"); } }
微信类 Weixin.java
package strategy; class Weixin extends PayBase{ public void payAction(){ System.out.println("微信支付"); } }
银联类 Yinlian.java
package strategy; class Yinlian extends PayBase{ public void payAction(){ System.out.println("银联支付"); } }
下面是抵扣接口和具体抵扣行为类:
抵扣接口 DikouBehavior.java
package strategy; public interface DikouBehavior { public void dikouAction(); }
代金券抵扣类Daijinquan.java
package strategy; public class DaiJinQuan implements DikouBehavior { public void dikouAction(){ System.out.println("代金券抵扣"); } }
积分抵扣类 JIfen.java
package strategy; public class JIfen implements DikouBehavior { public void dikouAction(){ System.out.println("积分抵扣"); } }
大家可以看到在客户端类里,可以自由选择任意一种抵扣方式,其他类代码均不用改动。当有新的抵扣方式加入时候,只要新增抵扣类。而客户端代码可以根据参数来选择抵扣方式。
这样的设计把经常变化的部分抽离出来封装,不变化的代码无需改动!!