zoukankan      html  css  js  c++  java
  • 别在再满屏的 if、else 了,试试策略模式,真香!!

    你还在写满屏的 if/ else/ switch 之类的判断逻辑吗?

    栈长在开发人员的代码中看过太多这样的低级代码了,真的太 low,极不好维护,本文栈长就教你如何用策略模式干掉 if/ else/ switch,让你的代码更优雅。

    什么是策略模式?

    比如说对象的某个行为,在不同场景中有不同的实现方式,这样就可以将这些实现方式定义成一组策略,每个实现类对应一个策略,在不同的场景就使用不同的实现类,并且可以自由切换策略。

    策略模式结构图如下:

    策略模式需要一个策略接口,不同的策略实现不同的实现类,在具体业务环境中仅持有该策略接口,根据不同的场景使用不同的实现类即可。

    面向接口编程,而不是面向实现。

    策略模式的优点:

    1、干掉繁琐的 if、switch 判断逻辑;

    2、代码优雅、可复用、可读性好;

    3、符合开闭原则,扩展性好、便于维护;

    策略模式的缺点:

    1、策略如果很多的话,会造成策略类膨胀;

    2、使用者必须清楚所有的策略类及其用途;

    策略模式实战

    举个实际的例子,XX 公司是做支付的,根据不同的客户类型会有不同的支付方式和支付产品,比如:信用卡、本地支付,而本地支付在中国又有微信支付、支付宝、云闪付、等更多其他第三方支付公司,这时候策略模式就派上用场了。

    传统的 if/ else/ switch 等判断写法大家都会写,这里就不贴代码了,直接看策略模式怎么搞!

    1、定义策略接口

    定义一个策略接口,所有支付方式的接口。

    策略接口:

    /**
     * 支付接口
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    public interface IPayment {
    
        /**
         * 支付
         * @param order
         * @return
         */
        PayResult pay(Order order);
    
    }
    

    订单信息类:

    /**
     * 订单信息
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    @Data
    public class Order {
    
        /**
         * 金额
         */
        private int amount;
    
        /**
         * 支付类型
         */
        private String paymentType;
    
    }
    

    返回结果类:

    /**
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    @Data
    @AllArgsConstructor
    public class PayResult {
    
        /**
         * 支付结果
         */
        private String result;
    
    }
    

    2、定义各种策略

    定义各种支付策略,微信支付、支付宝、云闪付等支付实现类都实现这个接口。

    微信支付实现:

    /**
     * 微信支付
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    @Service("WechatPay")
    public class WechatPay implements IPayment {
    
        @Override
        public PayResult pay(Order order) {
            return new PayResult("微信支付成功");
        }
    
    }
    

    支付宝实现:

    /**
     * 支付宝
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    @Service("Alipay")
    public class Alipay implements IPayment {
    
        @Override
        public PayResult pay(Order order) {
            return new PayResult("支付宝支付成功");
        }
    
    }
    

    云闪付实现:

    /**
     * 银联云闪付
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    @Service("UnionPay")
    public class UnionPay implements IPayment {
    
        @Override
        public PayResult pay(Order order) {
            return new PayResult("云闪付支付成功");
        }
    
    }
    

    这里我把所有支付方式类都用 @Service 注解生成 Bean 放入 Spring Bean 容器中了,在使用策略的时候就不用 new 支付对象了,可以直接使用 Bean,这样更贴近业务。Spring 基础教程就不介绍了,大家可以关注公众号Java技术栈,回复:spring,历史教程我都整理好了。

    3、使用策略

    有的文章使用了枚举、HashMap 的方式来根据策略名称映射策略实现类 ,这样是没有问题,但在使用了 Spring 框架的项目还是有点多此一举,完全可以发挥 Spring 框架的优势,使用 Bean 名称就能找到对应的策略实现类了。

    参考示例代码如下:

    /**
     * 支付服务
     * @author: 栈长
     * @from: 公众号Java技术栈
     */
    @RestController
    public class PayService {
    
        @Autowired
        private ApplicationContext applicationContext;
    
        /**
         * 支付接口
         * @param amount
         * @param paymentType
         * @return
         */
        @RequestMapping("/pay")
        public PayResult pay(@RequestParam("amount") int amount,
                        @RequestParam("paymentType") String paymentType) {
            Order order = new Order();
            order.setAmount(amount);
            order.setPaymentType(paymentType);
    
            // 根据支付类型获取对应的策略 bean
            IPayment payment = applicationContext.getBean(order.getPaymentType(), IPayment.class);
    
            // 开始支付
            PayResult payResult = payment.pay(order);
    
            return payResult;
        }
    
    }
    

    看示例代码,我并没有像策略模式结构图中那样新建一个 Context 类持有策略接口,那是标准的策略模式,其实道理是一样的,关键是怎么施放策略。

    测试一下:

    http://localhost:8080/pay?amount=8800&paymentType=WechatPay

    测试 OK,传入不同的支付方式会调用不同的策略。

    本节教程所有实战源码已上传到这个仓库:https://github.com/javastacks/javastack

    策略模式在 JDK 中的应用

    现在我们知道如何使用策略模式了,现在我们再看看 JDK 哪些地方运用了策略模式呢。

    1、线程池中的拒绝策略

    线程池的构造中有一个拒绝策略参数,默认是默认拒绝策略:

    其实这就是一个策略接口:

    下面有几种拒绝策略的实现:

    image-20210329161322406

    在创建线程池的时候,就可以传入不同的拒绝策略,这就是 JDK 中策略模式的经典实现了。

    2、比较器

    JDK 中大量使用了 Comparator 这个策略接口:

    策略接口有了,但策略需要开发人员自己定。

    集合排序我们比较熟悉的了,不同的排序规则其实就是不同的策略:

    这个策略模式使用了函数式编程接口,比较规则使用匿名内部类或者 Lambda 表达式就搞定了,不需要每个规则定义一个实现类,这样就大量省略策略类了。

    这个策略模式可能藏的比较深,但也是 JDK 中经典的策略模式的应用了。

    不限于这两个,其实还有更多,你还知道别的么?欢迎留言分享……

    所以说,策略模式就在你身边,你一直都在用,但可能没有发觉。。

    总结

    使用策略模式,我们可以轻松干掉大量的 if/ else,代码也更优雅,还能很灵活的扩展。

    像本文中支付的案例,后面我们想添加、删除多少个支付方式都不用修改现有的代码,所以就不会影响现有的业务,真正做到对扩展开放,对修改关闭。

    当然,完全干掉 if/ else 是不可能的,不能过度设计,不能为了使用设计模式而使用设计模式,否则适得其反。但是,我们每个程序员都需要掌握策略模式,做到在系统中灵活驾驭,这样才能写出更优雅、高质量的代码。

    本节教程所有实战源码已上传到这个仓库:

    https://github.com/javastacks/javastack

    好了,今天的分享就到这里了,后面栈长我会更新其他设计模式的实战文章,公众号Java技术栈第一时间推送。Java技术栈《设计模式》系列文章陆续更新中,请大家持续关注哦!

    最后,觉得我的文章对你用收获的话,动动小手,给个在看、转发,原创不易,栈长需要你的鼓励。

    版权申明:本文系公众号 "Java技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重他人劳动成果和知识产权。

    近期热文推荐:

    1.600+ 道 Java面试题及答案整理(2021最新版)

    2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!

    3.阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

    4.Spring Cloud 2020.0.0 正式发布,全新颠覆性版本!

    5.《Java开发手册(嵩山版)》最新发布,速速下载!

    觉得不错,别忘了随手点赞+转发哦!

  • 相关阅读:
    (转)【web前端培训之前后端的配合(中)】继续昨日的故事
    ural(Timus) 1136. Parliament
    scau Josephus Problem
    ACMICPC Live Archive 6204 Poker End Games
    uva 10391 Compound Words
    ACMICPC Live Archive 3222 Joke with Turtles
    uva 10132 File Fragmentation
    uva 270 Lining Up
    【转】各种字符串哈希函数比较
    uva 10905 Children's Game
  • 原文地址:https://www.cnblogs.com/javastack/p/14776760.html
Copyright © 2011-2022 走看看