谈笑间学会策略模式

1 背景

某天早上,Skr郎正在一边悠哉的吃着加了三个鸡蛋的手抓饼,一边悠闲地逛着论坛,看着沙雕网友的帖子,Skr郎会心一笑,正欲给沙雕帖子点赞,邮件忽的弹出,Skr郎慢悠悠的打开邮件

任务: 对接阿里支付接口
时限: 一周

要求如下:
	1. 单笔限额不超过1W
	2. 可以成功提现
	3. 发起提现后,可以主动查询提现结果

Skr郎心中暗喜,看我两天搞定,剩下的时间就可以嘿嘿嘿了

/**
 * 阿里业务处理
 *
 * @author gp6
 * @date 2020/3/13
 */
public class AlibabaHandler {

    void singleQuota() {
        System.out.println("阿里校验单笔限额");
    }

    void pay() {
        System.out.println("阿里支付逻辑");
    }

    void getResult() {
        System.out.println("阿里主动查询支付结果");
    }
}


/**
 * 支付服务类
 *
 * @author gp6
 * @date 2020/3/13
 */
public class PayService {

    public static void main(String[] args) {
       // 支付接口(第一个方法)
        AlibabaHandler alibabaHandler = new AlibabaHandler();
        alibabaHandler.singleQuota();
        alibabaHandler.pay();

        // 查询接口(第二个方法)
        AlibabaHandler alibabaHandler1 = new AlibabaHandler();
        alibabaHandler1.getResult();
    }
}

Skr郎看着完成的代码,心中评价道:这段代码,逻辑严谨,注释清晰,优雅中透露着洒脱,洒脱中透露着不羁,完美!

正洋洋自得间,项目经理喊道:Skr郎,你过来一下!Skr郎赶紧屁颠屁颠的跑过去.

项目经理:临时加个需求,把微信支付也接进来,根据前台传的渠道,使用不同的支付方式,时间不加.

Skr郎:好的,没问题(MMP)

/**
 * 微信业务处理
 *
 * @author gp6
 * @date 2020/3/13
 */
public class WechatHandler {

    void singleQuota() {
        System.out.println("微信校验单笔限额");
    }

    void pay() {
        System.out.println("微信支付逻辑");
    }

    void getResult() {
        System.out.println("微信主动查询支付结果");
    }
}


/**
 * 支付服务类
 *
 * @author gp6
 * @date 2020/3/13
 */
public class PayService {

    public static void main(String[] args) {
        // 前台传的渠道 1:阿里 2:微信
        String channel = "1";

        // 支付逻辑(第一个方法)
        if ("1".equals(channel)) {
            // 阿里支付接口
            AlibabaHandler alibabaHandler = new AlibabaHandler();
            alibabaHandler.singleQuota();
            alibabaHandler.pay();
        } else if ("2".equals(channel)) {
            // 微信支付接口
            WechatHandler wechatHandler = new WechatHandler();
            wechatHandler.singleQuota();
            wechatHandler.pay();
        }


        // 查询接口(第二个方法)
        if ("1".equals(channel)) {
            // 阿里查询
            AlibabaHandler alibabaHandler = new AlibabaHandler();
            alibabaHandler.getResult();
        } else if ("2".equals(channel)) {
            // 微信查询
            WechatHandler wechatHandler = new WechatHandler();
            wechatHandler.getResult();
        }
    }
}


Skr郎看着完成的代码,心中评价道:这段代码,逻辑严谨,注释清晰,优雅中透露着洒脱,洒脱中透露着不羁,虽然没有上一版优雅,但还是比较不错的!

正洋洋自得间,又听项目经理喊道:Skr郎,你过来一下!Skr郎心里想着如果这次加需求不加时间,老子坚决不做!

项目经理:临时在加个需求,把通联和Ping++支付也接进来,根据前台传的渠道,使用不同的支付方式,时间不加.

Skr郎:好的,没问题(心里问候一下他的族中长辈)


/**
 * Ping++业务处理
 *
 * @author gp6
 * @date 2020/3/13
 */
public class PingHandler {

    void singleQuota() {
        System.out.println("Ping++校验单笔限额");
    }

    void pay() {
        System.out.println("Ping++支付逻辑");
    }

    void getResult() {
        System.out.println("Ping++主动查询支付结果");
    }
}


/**
 * 通联业务处理
 *
 * @author gp6
 * @date 2020/3/13
 */
public class AllinpayHandler {

    void singleQuota() {
        System.out.println("通联校验单笔限额");
    }

    void pay() {
        System.out.println("通联支付逻辑");
    }

    void getResult() {
        System.out.println("通联主动查询支付结果");
    }
}


/**
 * 支付服务类
 *
 * @author gp6
 * @date 2020/3/13
 */
public class PayService {

    public static void main(String[] args) {
        // 前台传的渠道 1:阿里 2:微信 3:Ping++ 4:通联
        String channel = "1";

        // 支付逻辑(第一个方法)
        if ("1".equals(channel)) {
            // 阿里支付接口
            AlibabaHandler alibabaHandler = new AlibabaHandler();
            alibabaHandler.singleQuota();
            alibabaHandler.pay();
        } else if ("2".equals(channel)) {
            // 微信支付接口
            WechatHandler wechatHandler = new WechatHandler();
            wechatHandler.singleQuota();
            wechatHandler.pay();
        }else if ("3".equals(channel)) {
            // Ping++支付接口
            PingHandler pingHandler = new PingHandler();
            pingHandler.singleQuota();
            pingHandler.pay();
        }else if ("4".equals(channel)) {
            // 通联支付接口
            AllinpayHandler allinpayHandler = new AllinpayHandler();
            allinpayHandler.singleQuota();
            allinpayHandler.pay();
        }


        // 查询接口(第二个方法)
        if ("1".equals(channel)) {
            // 阿里查询
            AlibabaHandler alibabaHandler = new AlibabaHandler();
            alibabaHandler.getResult();
        } else if ("2".equals(channel)) {
            // 微信查询
            WechatHandler wechatHandler = new WechatHandler();
            wechatHandler.getResult();
        }else if ("3".equals(channel)) {
            // Ping++查询
            PingHandler pingHandler = new PingHandler();
            pingHandler.getResult();
        }else if ("4".equals(channel)) {
            // 通联查询
            AllinpayHandler allinpayHandler = new AllinpayHandler();
            allinpayHandler.getResult();
        }
    }
}

Skr郎看着完成的代码,心中评价道:这段代码,逻辑严谨,注释清晰…我呸,这写的什么鬼东西!看着长长的if else,Skr郎陷入沉思…

如果后面再加新的渠道,是不是每次都要改动PayService中的调用逻辑,而且如果有别的方法要用此段逻辑,if else 又要复制一遍

脑中过滤着以往所学的设计模式…突然,灵光一闪,策略模式

2 策略模式

定义:
	定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户

看了策略模式的定义,是不是很符合这种场景

定义了一系列算法(支付逻辑),并将每个算法(支付逻辑)封装起来,使它们可以相互替换(根据不同channel切换不同的支付逻辑),且算法的变化不会影响使用算法的客户(PayService)

简直完美,Skr郎沾沾自喜

/**
 * 支付的抽象策略类
 * 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现
 *
 * @author gp6
 * @date 2020/3/13
 */
public interface PayHandler {

    /**
     * 单笔限额
     */
    void singleQuota();

    /**
     * 支付
     */
    void pay();

    /**
     * 提现结果查询
     */
    void getResult();
}


/**
 * 微信业务处理
 * 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现
 *
 * @author gp6
 * @date 2020/3/13
 */
public class WechatHandler implements PayHandler {
    public void singleQuota() {
        System.out.println("微信校验单笔限额");
    }

    public void pay() {
        System.out.println("微信支付逻辑");
    }

    public void getResult() {
        System.out.println("微信主动查询支付结果");
    }
}


/**
 * 阿里业务处理
 * 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现
 *
 * @author gp6
 * @date 2020/3/13
 */
public class AlibabaHandler implements PayHandler {

    public void singleQuota() {
        System.out.println("阿里校验单笔限额");
    }

    public void pay() {
        System.out.println("阿里支付逻辑");
    }

    public void getResult() {
        System.out.println("阿里主动查询支付结果");
    }
}

通联与Ping++代码类似


/**
 * 环境(Context)类:持有一个策略类的引用,最终给客户端调用
 *
 * @author gp6
 * @date 2020/3/13
 */
public class Context {

    private PayHandler payHandler;

    public Context(PayHandler payHandler){
        this.payHandler = payHandler;
    }

    /**
     * 单笔限额
     */
    void singleQuota() {
        payHandler.singleQuota();
    }

    /**
     * 支付
     */
    void pay() {
        payHandler.pay();
    }

    /**
     * 提现结果查询
     */
    void getResult() {
        payHandler.getResult();
    }
}


/**
 * 支付服务类
 *
 * @author gp6
 * @date 2020/3/13
 */
public class PayService {

    public static void main(String[] args) {
        // 前台传的渠道 1:阿里 2:微信 3:Ping++ 4:通联
        Integer channel = 1;

        // 支付逻辑(第一个方法)
        Context context;
        switch (channel) {
            case 1:
                context = new Context(new AlibabaHandler());
                break;
            case 2:
                context = new Context(new WechatHandler());
                break;
            case 3:
                context = new Context(new PingHandler());
                break;
            case 4:
                context = new Context(new AllinpayHandler());
                break;
            default:
                throw new RuntimeException("渠道不支持!");
        }
        context.singleQuota();
        context.pay();

        // 查询接口(第二个方法)
        Context context2;
        switch (channel) {
            case 1:
                context2 = new Context(new AlibabaHandler());
                break;
            case 2:
                context2 = new Context(new WechatHandler());
                break;
            case 3:
                context2 = new Context(new PingHandler());
                break;
            case 4:
                context2 = new Context(new AllinpayHandler());
                break;
            default:
                throw new RuntimeException("渠道不支持!");
        }
        context2.getResult();
    }
}

使用了策略模式,但是调用者(PayService)中,还是有重复的大量的if else

根据不用的渠道创建不同的具体策略类(new AlibabaHandler()),这不是典型的工厂模式吗…

3 策略模式结合工厂模式

将工厂模式放入策略环境中

/**
 * 环境(Context)类:持有一个策略类的引用,最终给客户端调用
 *
 * @author gp6
 * @date 2020/3/13
 */
public class Context {

    private PayHandler payHandler;

    public Context(Integer channel){
        switch (channel) {
            case 1:
                payHandler = new AlibabaHandler();
                break;
            case 2:
                payHandler = new WechatHandler();
                break;
            case 3:
                payHandler = new PingHandler();
                break;
            case 4:
                payHandler = new AllinpayHandler();
                break;
            default:
                throw new RuntimeException("渠道不支持!");
        }
    }

    /**
     * 单笔限额
     */
    void singleQuota() {
        payHandler.singleQuota();
    }

    /**
     * 支付
     */
    void pay() {
        payHandler.pay();
    }

    /**
     * 提现结果查询
     */
    void getResult() {
        payHandler.getResult();
    }
}


/**
 * 支付服务类
 *
 * @author gp6
 * @date 2020/3/13
 */
public class PayService {

    public static void main(String[] args) {
        // 前台传的渠道 1:阿里 2:微信 3:Ping++ 4:通联
        Integer channel = 1;

        // 支付逻辑(第一个方法)
        Context context = new Context(channel);
        context.singleQuota();
        context.pay();

        // 查询接口(第二个方法)
        Context context2  = new Context(channel);
        context2.getResult();
    }
}

此时如果再添加渠道,只需在Context的构造方法的判断逻辑中添加新的渠道,然后在相应的支付逻辑类中写入相应逻辑,只在策略环境中存在if else,调用者无需写重复的if else


 switch (channel) {
    case 1:
        payHandler = new AlibabaHandler();
        break;
    case 2:
        payHandler = new WechatHandler();
        break;
    case 3:
        payHandler = new PingHandler();
        break;
    case 4:
        payHandler = new AllinpayHandler();
        break;

    // 新的渠道
    case 5:
    	payHandler = new XXXHandler();
    	break;
    default:
        throw new RuntimeException("渠道不支持!");
}

/**
 * 新渠道的业务逻辑处理
 * 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现
 *
 * @author gp6
 * @date 2020/3/13
 */
public class XXXHandler implements PayHandler{
	.....
}

写到此处Skr郎欣喜的点点头,暗自得意,正打算将代码提交,忽然眉头一皱,每次添加新的渠道,都要修改判断逻辑,不是违背了 开闭原则 吗
Skr再次沉思

4 使用反射遵循开闭原则

/**
 * 环境(Context)类:持有一个策略类的引用,最终给客户端调用
 *
 * @author gp6
 * @date 2020/3/13
 */
public class Context {

    private PayHandler payHandler;

    public Context(PayEnum payEnum){
        try {
            payHandler= (PayHandler) Class.forName(payEnum.getClassName()).newInstance();
        } catch (Exception e) {
            throw new RuntimeException("渠道错误!");
        }
    }


    /**
     * 单笔限额
     */
    void singleQuota() {
        payHandler.singleQuota();
    }

    /**
     * 支付
     */
    void pay() {
        payHandler.pay();
    }

    /**
     * 提现结果查询
     */
    void getResult() {
        payHandler.getResult();
    }
}


/**
* 策略枚举
*
* @author gp6
* @date 2020/3/13
*/
public enum PayEnum {

    /**
     * 渠道对应类全路径
     */
    OPERATION_ADD("com.gp6.pay.AlibabaHandler", 1),
    OPERATION_SUB("com.gp6.pay.WechatHandler", 2),
    OPERATION_MUL("com.gp6.pay.PingHandler", 3);

    PayEnum(String className, Integer channel) {
        this.className = className;
        this.channel = channel;
    }

    /**
     * 类的全路径
     */
    private String className;

    /**
     * 渠道
     */
    private Integer channel;

    /**
     * 匹配渠道
     *
     * @param channel 渠道
     * @return 相关枚举
     */
    public static PayEnum matchChannel(Integer channel) {
        for (PayEnum payEnum : PayEnum.values()) {
            if (payEnum.getChannel().equals(channel)) {
                return payEnum;
            }
        }
        throw new RuntimeException("渠道不支持!");
    }

    public String getClassName() {
        return className;
    }

    public Integer getChannel() {
        return channel;
    }
}



/**
 * 支付服务类
 *
 * @author gp6
 * @date 2020/3/13
 */
public class PayService {

    public static void main(String[] args) {
        // 前台传的渠道 1:阿里 2:微信 3:Ping++ 4:通联
        Integer channel = 1;

        // 支付逻辑(第一个方法)
        Context context = new Context(PayEnum.matchChannel(channel));
        context.singleQuota();
        context.pay();

        // 查询接口(第二个方法)
        Context context2 = new Context(PayEnum.matchChannel(channel));
        context2.getResult();
    }
}

此时,如果想添加通联的渠道只需在PayEnum中添加

OPERATION_MUL("com.gp6.pay.AllinpayHandler", 4)

然后在com.gp6.pay.AllinpayHandler中撰写相应逻辑即可

Skr郎写到此处,心满意足的点点头,心中评价道:这段代码,逻辑严谨,注释清晰,优雅中透露着洒脱,洒脱中透露着不羁,真是"此码只应天上有,人间难得几回寻!"
呀! 想不到我作诗的功夫也如此深厚,哎,如此完美的男人,别人只能羡慕嫉妒恨!

5 策略模式类图

策略模式

StrategyContext相当于文章中的Context
StrategyOperation相当于文章中的PayHandle
StrategyOperationAdd与StrategyOperationMul相当于文章中的AlibabaHandler/AllinpayHandler/PingHandler/WechatHandler

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值