策略模式是一种软件设计模式, 是指对象有某个行为, 但是在不同的场合会有不同的实现算法, 用编程语言来说, 就是说这个类的行为和算法会在运行时作出改变. 这种模式属于行为型模式. 在我们生活中有这样的例子, 比如说商场促销活动, 不同的商品有这不同的促销方案八折, 五折, 生日卡, 我们去旅行时, 可以选择飞机, 火车, 大巴等多种出行方案, 支付的时候可以选择微信, 支付宝, 银联等等.
这种场景下, 根据用户的需求需要对这个算法作出选择, 用户不知道这个算法的具体实现, 但是用户知道自己选择哪个算法.
我们以支付为例, 先来看看不使用策略模式的代码有什么弊端.
比如说现在有一个购物类, 从买东西到下单付款都有一系列的流程, 加入东西都买好了, 现在要付款了.
- /**
- * 支付类
- */
- public class PayType {
- public void payMoney(String type){
- if ("支付宝".equals(type)) {
- System.out.println("支付宝支付, 消费 200, 余额 20");
- }else if ("微信".equals(type)){
- System.out.println("微信支付, 消费 200, 余额 10");
- }else if ("银联".equals(type)){
- System.out.println("银联支付, 消费 200, 余额 3000");
- }
- }
- }
- /**
- * 客户端调用
- */
- public class Strategy {
- public static void main(String[] args) {
- PayType payType = new PayType();
- String type ="支付宝";
- // 选择支付宝支付
- payType.payMoney(type);
- }
- }
我们可以看到, 这个支付类有很多 if else 的判断语句, 假如我需要再添加一种支付方式, 就需要再次添加一个 if 判断, 这种方式不但使得支付类语句变得复杂, 不易维护, 而且也违背了开闭原则.
如果采用策略模式就能够很好的解决这个问题. 我们来看下策略模式怎么写.
先看下策略模式的定义: 该模式定义了一系列算法, 并将每个算法封装起来, 使它们可以相互替换, 且算法的变化不会影响使用算法的客户. 策略模式属于对象行为模式, 它通过对算法进行封装, 把使用算法的责任和算法的实现分割开来, 并委派给不同的对象对这些算法进行管理.
这里面有几个关键字, 具体理解是将每一种算法单独作为一个类, 每次新增一个算法就新增一个策略类, 修改某个算法不需要修改整个策略类, 直接修改对应的算法即可.
策略模式的主要结构如下:
抽象策略类 (Strategy): 定义一个公共的算法接口, 每种算法都实现了这个接口, 环境角色使用这个接口调用不同的算法, 一般使用接口或抽象类实现.
具体策略 (Concrete Strategy): 实现了这个算法接口的具体实现类.
环境类 (Context): 持有一个策略类的引用, 最终由客户端调用.
策略接口:
- /**
- * 支付接口 (策略接口)
- */
- public interface PayType {
- public void payMoney();
- }
具体的策略类:
- /**
- * 微信支付 (具体的策略类)
- */
- public class WeiChart implements PayType {
- @Override
- public void payMoney() {
- System.out.println("使用微信支付, 付款 200 元");
- }
- }
- /**
- * 支付宝支付 (具体的策略类)
- */
- public class Ali implements PayType {
- @Override
- public void payMoney() {
- System.out.println("使用支付宝支付, 付款金额 200 元");
- }
- }
- /**
- * 银联卡支付 (具体的策略类)
- */
- public class BlankCard implements PayType {
- @Override
- public void payMoney() {
- System.out.println("使用银行卡支付, 付款金额 200, 余额 20 元");
- }
- }
环境类, 上下文对象:
- /**
- * 上下文对象, 持有策略类的引用
- */
- public class Context {
- PayType payType;
- public Context(PayType payType) {
- this.payType = payType;
- }
- public PayType getPayType() {
- return payType;
- }
- public void setPayType(PayType payType) {
- this.payType = payType;
- }
- }
客户端调用:
- /**
- * 客户端调用
- */
- public class Strategy {
- public static void main(String[] args) {
- // 使用微信支付
- Context context1 = new Context(new WeiChart());
- context1.getPayType().payMoney();
- // 使用银联卡支付
- Context context2 = new Context(new BlankCard());
- context2.getPayType().payMoney();
- }
- }
使用微信支付, 付款 200 元
使用银行卡支付, 付款金额 200, 余额 20 元
那么, 这里有个疑问, 为什么不能直接使用接口引用指向具体的实现类, 还需要再加一层 Context 类呢?
我们看下这个类的定义, 上下文对象, 这个上下文对象是干嘛的, 顾名思义, 承上启下, 上指的是客户端调用类, 下指的就是我们这个策略类.
在这里, 将实例化具体策略的过程由客户端转到 Context 类中, 客户端只需要和 Context 类交互即可, 使得支付算法和客户端彻底分离, 更加降低了客户端和策略类之间的耦合.
看下 UML 类图:
来源: https://www.cnblogs.com/JackSparrow-/p/10733062.html