别再写满屏的 if、else 了,试试策略模式

2023-11-05

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

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

什么是策略模式?

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

策略模式结构图如下:

别再写满屏的 if、else 了,试试策略模式,真香

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

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

策略模式的优点:

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技术栈,历史教程我都整理好了。

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?amo...

别再写满屏的 if、else 了,试试策略模式,真香

别再写满屏的 if、else 了,试试策略模式,真香

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

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

策略模式在 JDK 中的应用

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

1、线程池中的拒绝策略

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

别再写满屏的 if、else 了,试试策略模式,真香

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

别再写满屏的 if、else 了,试试策略模式,真香

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

别再写满屏的 if、else 了,试试策略模式,真香

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

2、比较器

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

别再写满屏的 if、else 了,试试策略模式,真香

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

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

别再写满屏的 if、else 了,试试策略模式,真香

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

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

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

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

总结

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

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

别再写满屏的 if、else 了,试试策略模式 的相关文章

随机推荐

  • qt下实现文件的拖拽打开

    引言 此文用于记录按下鼠标左键不放 拖动文件到程序中打开 该示例中只设置了可以拖动的文件类型为 h cpp txt这三种文件类型 程序运行的效果 示例 下面是具体的实现 项目的结构 具体的实现代码 如下 main cpp include d
  • 网络层之IP协议详解

    网络层 说简单点 就是在复杂的网络环境中确定一个合适的路径 我们来了解一下网络层中一个重要的协议 IP协议 IP协议 1 概念 IP协议是TCP IP协议簇中的核心协议 也是TCP IP的载体 所有的TCP UDP ICMP及IGMP数据都
  • 自定义期间查询数据库

    Python连接数据库匹配 import pandas as pd import numpy as np import pymysql import datetime 连接mysql conn pymysql connect host xx
  • KyLin的网页界面使用

    1 美图 上一篇 http blog csdn net qq 21383435 article details 75198823 1 根据上一张分析的内容得知 涉及到的字段是 pro表的 字段 ID 商品名称 价格 购买数量 付款 类别ID
  • 快速基于nodeJS+vue+vuex+mysql+redis建立一个后台管控系统

    structure admin structure admin是一个后台管控系统的架子 技术栈 nodeJS vue vuex mysql redis 前端使用vue的element ui的组件库 后端使用nodeJS的服务 数据库mysq
  • 求一行字符串的长度。(C语言)

    代码 include
  • hadoop集群搭建

    文章目录 一 基本配置 所有节点 一 配置静态网络 二 修改主机名和修改host文件 三 禁用SELINUX 四 关闭防火墙 并取消开机自启动 五 配置NTP时间同步 集群所有节点 六 下载一下vim编辑器 七 安装JDK1 8 八 创建h
  • 【Mysql高级】【第十五章】【锁】

    锁 1 概述 2 Mysql并发实务访问相同记录 2 1 读 读 2 2 写 写 2 3 读 写或者写 读 2 4 并发问题的解决方案 3 锁的不同角度分类 3 1 从数据操作的类型划分 读锁 写锁 0 行级别x锁 和 s锁的兼容性问题 1
  • qt connect多次

    1 坑的现象 有时项目中一个信号发出 对应连接的槽函数会执行多次 普通刷新界面都不会有问题 但是特别频繁的就会影响性能 如果是改变数据的 更有甚者会异常崩溃 2 遇坑的原因 qt中同一实例的同一信号和槽 connect多次 当信号发出时 槽
  • AD9 PCB文件黑色区域如何改变?

    PCB的大小是由KeepOut层定义的 用机械层定义有时候 黑色区域无这方面的意义 改变黑色区域大小可以用Design BoardShape RedefineBoardSharp来完成
  • 国内外常用公共NTP网络时间同步服务器地址

    目录 太长不看 NTP Pool Project NTP ORG CN NTP授时快速域名服务 HSDN Home Server Data Network 本地服务器数据网络 企业 阿里巴巴 腾讯 微软 苹果 谷歌 Facebook Clo
  • 从零开始学C++之STL(一):STL六大组件简介

    一 STL简介 一 泛型程序设计 泛型编程 generic programming 将程序写得尽可能通用 将算法从数据结构中抽象出来 成为通用的 C 的模板为泛型程序设计奠定了关键的基础 二 什么是STL 1 STL Standard Te
  • 高校圆桌派-解惑关于IT行业的3+N个问题

    高校圆桌派 话题风暴等你来 即日起参与 高校圆桌派 活动 就有机会获得CSDN高校圆桌大礼包和CSDN周边礼品免费包邮送到家 关于高校圆桌派 高校圆桌派活动是由CSDN高校俱乐部官方发起 征集同学们感兴趣的IT行业问题或大家最关心的热门话题
  • matlab中imrote,基于MATLAB的车牌识别系统的设计与研究

    基于MATLAB的车牌识别系统的设计与研究 基于MATLAB的车牌识别系统的设计与研究 摘要 汽车牌照自动识别系统是智能交通系统的重要组成部分 主要包括图像采集 图像预处理 车牌定位 字符分割 字符识别等五个核心部分 并提出了一套基于MAT
  • html左边多级菜单导航栏,精美的多级侧边栏导航菜单jQuery插件

    这是一款基于bootstrap的精美多级侧边栏导航菜单jQuery插件 该导航菜单在bootstrap样式的基础上 通过jQuery来为导航菜单绑定菜单点击事件 生成非常漂亮的多级侧边栏导航菜单 使用方法 在页面中引入bootstrap样式
  • prometheus监控k8s kube-proxy target down

    prometheus kube proxy target down 解决 修改配置 kubectl edit cm kube proxy n kube system metricsBindAddress 0 0 0 0 10249 删除 k
  • 2050年全部人口的86%集中到城市,智慧城市的五项关键技术

    本文翻译至 http readwrite jp cities 32108 人口的城市化毫不停息 人们的住所越来越多地从地方移动到城市 到2050年为止预计发达国家人口的86 发展中国家人口的64 将住在城市 数量有限的城市要负担如此多的人口
  • 查看并设置Linux的IP地址

    ip addr 查看网卡分配情况 如发现IP地址为 127 0 0 1 这里要修改ip地址 修改IP地址方法 1 进入 etc sysconfig network scripts 注 不同版本ifcfg ens33文件名可能会不一样 2 修
  • Visual C++ MFC的图形绘制——常见问题汇总

    Visual C MFC的图形绘制 常见问题汇总 目录 一 常见问题 1 菜单界面制作 2 命令响应函数 3 添加私有变量 4 消息响应函数 二 后记 三 补充代码 一 常见问题 1 菜单界面制作 题目描述 新建一个单文档类型的MFC Ap
  • 别再写满屏的 if、else 了,试试策略模式

    你还在写满屏的 if else switch 之类的判断逻辑吗 栈长在开发人员的代码中看过太多这样的低级代码了 真的太 low 极不好维护 本文栈长就教你如何用策略模式干掉 if else switch 让你的代码更优雅 什么是策略模式 比