设计模式第九讲:常见重构技巧 - 去除不必要的!=

设计模式第九讲:常见重构技巧 - 去除不必要的!=

项目中会存在大量判空代码,多么丑陋繁冗!如何避免这种情况?我们是否滥用了判空呢?本文是设计模式第九讲,讲解常见重构技巧:去除不必要的!=

文章目录

  • 设计模式第九讲:常见重构技巧 - 去除不必要的!=
    • 1、场景一:null无意义之常规判断空
    • 2、场景二:null无意义之使用断言Assert
    • 3、场景三:写util类是否都需要逐级判断空?
    • 4、场景四:让null变的有意义
    • 5、场景五:Java8中使用Optional(推荐)

1、场景一:null无意义之常规判断空

  • 通常是这样的
private void xxxMethod(String key){if(key != null && !"".equals(key)){// do something}
}
  • 初步的,使用Apache Commons,Guvava,Hutool等 StringUtils
private void xxxMethod(String key){if(StringUtils.isNotEmpty(key)){// do something}
}

2、场景二:null无意义之使用断言Assert

  • 考虑用Assert断言
private void xxxMethod(String key){Assert.notNull(key);// do something
}

3、场景三:写util类是否都需要逐级判断空?

逐级判断空,还是抛出自定义异常,还是不处理?It Depends…

hutool IdcardUtil 显然是交给调用者判断的。

/*** 是否有效身份证号** @param idCard 身份证号,支持18位、15位和港澳台的10位* @return 是否有效*/
public static boolean isValidCard(String idCard) {idCard = idCard.trim();// 这里idCard没判断空int length = idCard.length();switch (length) {case 18:// 18位身份证return isValidCard18(idCard);case 15:// 15位身份证return isValidCard15(idCard);case 10: {// 10位身份证,港澳台地区String[] cardVal = isValidCard10(idCard);return null != cardVal && "true".equals(cardVal[2]);}default:return false;}
}
  • 再比如 Apache Common IO中, 并没判断空
/*** Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>.* @param input the byte array to read from* @param output the <code>OutputStream</code> to write to* @throws IOException In case of an I/O problem*/
public static void copy(final byte[] input, final OutputStream output)throws IOException {output.write(input);
}

4、场景四:让null变的有意义

返回一个空对象(而非null对象),比如NO_ACTION是特殊的Action,那么我们就定义一个ACTION。下面举个例子,假设有如下代码

public interface Action {void doSomething();
}public interface Parser {Action findAction(String userInput);
}

其中,Parse有一个接口FindAction,这个接口会依据用户的输入,找到并执行对应的动作。假如用户输入不对,可能就找不到对应的动作(Action),因此findAction就会返回null,接下来action调用doSomething方法时,就会出现空指针。

解决这个问题的一个方式,就是使用 Null Object pattern(空对象模式)

NullObject模式首次发表在“ 程序设计模式语言 ”系列丛书中。一般的,在面向对象语言中,对对象的调用前需要使用判空检查,来判断这些对象是否为空,因为在空引用上无法调用所需方法。

img

我们来改造一下

类定义如下,这样定义findAction方法后,确保无论用户输入什么,都不会返回null对象:

public class MyParser implements Parser {private static Action NO_ACTION = new Action() {public void doSomething() { /* do nothing */ }};public Action findAction(String userInput) {// ...if ( /* we can't find any actions */ ) {return NO_ACTION;}}
}

对比下面两份调用实例

1.冗余: 每获取一个对象,就判一次空

Parser parser = ParserFactory.getParser();
if (parser == null) {// now what?// this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {// do nothing} 
else {action.doSomething();
}

2.精简

ParserFactory.getParser().findAction(someInput).doSomething();

因为无论什么情况,都不会返回空对象,因此通过findAction拿到action后,可以放心地调用action的方法。

顺便再提下一个插件:

.NR Null Object插件 NR Null Object是一款适用于Android Studio、IntelliJ IDEA、PhpStorm、WebStorm、PyCharm、RubyMine、AppCode、CLion、GoLand、DataGrip等IDEA的Intellij插件。其可以根据现有对象,便捷快速生成其空对象模式需要的组成成分,其包含功能如下:

  • 分析所选类可声明为接口的方法;
  • 抽象出公有接口;
  • 创建空对象,自动实现公有接口;
  • 对部分函数进行可为空声明;
  • 可追加函数进行再次生成;
  • 自动的函数命名规范

5、场景五:Java8中使用Optional(推荐)

假设我们有一个像这样的类层次结构:

class Outer {Nested nested;Nested getNested() {return nested;}
}
class Nested {Inner inner;Inner getInner() {return inner;}
}
class Inner {String foo;String getFoo() {return foo;}
}

解决这种结构的深层嵌套路径是有点麻烦的。我们必须编写一堆 null 检查来确保不会导致一个 NullPointerException:

Outer outer = new Outer();
if (outer != null && outer.nested != null && outer.nested.inner != null) {System.out.println(outer.nested.inner.foo);
}

我们可以通过利用 Java 8 的 Optional 类型来摆脱所有这些 null 检查。map 方法接收一个 Function 类型的 lambda 表达式,并自动将每个 function 的结果包装成一个 Optional 对象。这使我们能够在一行中进行多个 map 操作。Null 检查是在底层自动处理的。

Optional.of(new Outer()).map(Outer::getNested).map(Nested::getInner).map(Inner::getFoo).ifPresent(System.out::println);

还有一种实现相同作用的方式就是通过利用一个 supplier 函数来解决嵌套路径的问题:

Outer obj = new Outer();
resolve(() -> obj.getNested().getInner().getFoo()).ifPresent(System.out::println);

调用 obj.getNested().getInner().getFoo()) 可能会抛出一个 NullPointerException 异常。在这种情况下,该异常将会被捕获,而该方法会返回 Optional.empty()。

public static <T> Optional<T> resolve(Supplier<T> resolver) {try {T result = resolver.get();return Optional.ofNullable(result);}catch (NullPointerException e) {return Optional.empty();}
}

请记住,这两个解决方案可能没有传统 null 检查那么高的性能。不过在大多数情况下不会有太大问题。

  • 更多Optional,可以看这篇: Java8特性第三讲:如何使用Optional类优雅解决业务npe问题
    • Optional类的意义
    • Optional类有哪些常用的方法
    • Optional举例贯穿所有知识点
    • 多重类嵌套Null值判断

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/64333.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Swift 中的动态成员查找

文章目录 前言基础介绍基础示例1. 定义一个动态成员访问类&#xff1a;2. 访问嵌套动态成员&#xff1a; 使用 KeyPath 的编译时安全性KeyPath 用法示例KeyPath 进阶使用示例1. 动态访问属性&#xff1a;2. 结合可选属性和 KeyPath&#xff1a;3. 动态 KeyPath 和字典&#xff…

MySQL8.0.22安装过程记录(个人笔记)

1.点击下载MySQL 2.解压到本地磁盘&#xff08;注意路径中不要有中文&#xff09; 3.在解压目录创建my.ini文件 文件内容为 [mysql] # 设置mysql客户端默认字符集 default-character-setutf8[mysqld] # 设置端口 port 3306 # 设计mysql的安装路径 basedirE:\01.app\05.Tool…

《爵士乐史》乔德.泰亚 笔记

第一章 【美国音乐的非洲化】 【乡村布鲁斯和经典布鲁斯】 布鲁斯&#xff1a;不止包括忧愁、哀痛 十二小节布鲁斯特征&#xff1a; 1.乐型&#xff08;A:主、B:属、C/D:下属&#xff09;&#xff1a;A→A→B→A→C→D→A→A 2.旋律&#xff1a;大三、小三、降七、降五 盲人…

Matlab(GUI程式设计)

目录 1.MatlabGUI 1.1 坐标区普通按钮 1.1.1 对齐组件 1.1.2 按钮属性 1.1.3 脚本说明 1.1.4 选择呈现 1.3 编译GUI程序 在以前的时候&#xff0c;我们的电脑还是这样的 随着科技的不断进步&#xff0c;我们的电脑也发生着翻天覆地的改变1990s&#xff1a; 在未来&#xff0c…

优化爬虫请求:如何选择合适的爬虫ip轮换策略?

在进行爬虫任务时&#xff0c;使用隧道爬虫ip并采用合适的轮换策略可以提高稳定性和效率。选择合适的隧道爬虫ip轮换策略可以优化您的爬虫请求过程。 1、考量目标网站特点 不同网站对于频繁请求可能有不同限制或反爬机制。 了解目标网站是否存在IP封禁、验证码等问题&#xff…

2359. 找到离给定两个节点最近的节点;1781. 所有子字符串美丽值之和;2406. 将区间分为最少组数

2359. 找到离给定两个节点最近的节点 核心思想:统计node1和node2分别到每个点的距离&#xff0c;然后在枚举每个点统计结果。关键在于如何统计node到每个点的距离&#xff0c;首先是初始化为inf很重要&#xff0c;因为在枚举的时候&#xff0c;因为是inf代表了这个节点无法到达…

VC++使用Microsoft Speech SDK进行文字TTS朗读

Microsoft Speech SDK下载地址 https://www.microsoft.com/en-us/download/details.aspx?id10121 需要msttss22L.exe、SpeechSDK51.exe、SpeechSDK51LangPack.exe三个&#xff0c;下载后全部安装 使用VS2005建立一个win32控制台项目 朗读"hello word"、中文“你好”…

Flink的checkpoint是怎么实现的?

分析&回答 Checkpoint介绍 Checkpoint容错机制是Flink可靠性的基石,可以保证Flink集群在某个算子因为某些原因(如 异常退出)出现故障时,能够将整个应用流图的状态恢复到故障之前的某一状态,保证应用流图状态的一致性。Flink的Checkpoint机制原理来自“Chandy-Lamport alg…

Docker 的快速使用

ubuntu安装 centos安装 安装完毕之后执行一下这条命令&#xff0c;可以避免每次使用docker命令都需要sudo权限 sudo usermod -aG docker $USER阿里云docker镜像加速 DockerHub 遇到不懂或者不会使用的命令可以使用docker --help查看文档 docker --help 如&#xff1a; dock…

OFDM 系统在 AWGN 信道下对不同载波频率偏移 (CFO) 的 BER 灵敏度研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Metinfo6.0.0任意文件读取【漏洞复现】

文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现代码审计漏洞点 1.5、深度利用EXP编写 1.6、漏洞挖掘1.7修复建议 1.1、漏洞描述 漏洞名称&#xff1a;MetInfo任意文件读取 漏洞简介&#xff1a;MetInfo是一套使用PHP和MySQL开发的内容管理系统&#xff0c;其…

Java实现根据商品ID获取京东商品详情数据,1688商品详情接口,1688API接口封装方法

要通过京东的API获取商品详情数据&#xff0c;您可以使用京东开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例&#xff0c;展示如何通过京东开放平台API获取商品详情&#xff1a; 首先&#xff0c;确保您已注册成为京东开放平台的开发者&#xff0c;并创建一…

经管博士科研基础【12】包络定理

当我们知道一个函数的最优解时&#xff0c;我们要求解这一个函数的值函数关于函数中某一个参数的导数&#xff0c;那么就可以使用包络定理。 1. 无约束条件下的包络定理 函数在其极值点处对一个参数&#xff08;参数不是自变量&#xff09;取偏导数的结果&#xff0c;等价于这…

Node.js crypto模块 加密算法

背景 微信小程序调用飞蛾热敏纸打印机&#xff0c;需要进行参数sig签名校验&#xff0c;使用的是sha1进行加密 // 通过crypto.createHash()函数&#xff0c;创建一个hash实例&#xff0c;但是需要调用md5&#xff0c;sha1&#xff0c;sha256&#xff0c;sha512算法来实现实例的…

小兔鲜商02

npm i vueuse/core -fvue插件使用&#xff1a; 许多公用的全局组件&#xff0c;&#xff0c;可以通过插件注册进去&#xff0c;就不用一个一个导入组件&#xff0c;&#xff0c; import XtxSkeleton from /components/library/xtx-skeletonexport default {install (app) {// …

linux操作系统中的动静态库(未完)

1. 静态库与动态库 静态库&#xff08;.a&#xff09;&#xff1a;程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库动态库&#xff08;.so&#xff09;&#xff1a;程序在运行的时候才去链接动态库的代码&#xff0c;多个程序共享使用库的…

unity 跨屏显示

1.代码 /*Type:设置分辨率*/ using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.Runtime.InteropServices;public class ScreenManager : MonoBehaviour {[HideInInspector]//导入设置窗口函数 [DllImport("…

Redis之MoreKey问题及Scan命令解读

目录 MoreKey问题讨论 Scan命令 Sscan命令 Hscan命令 Zscan命令 MoreKey问题讨论 keys * 查看当前库所有key 对于海量数据执行key *会造成严重服务卡顿、影响业务。在实际环境中最好不要使用。生产制造过程中keys * / flushdb/flushall等危险命令以防止误删误用。 大量的…

kotlin实现猜数游戏

游戏规则 1.程序随机生成一个1到100的数字&#xff0c;作为MagicNumber 2.用户根据提示输入数据&#xff0c;只有三次机会输入数据 代码 代码很简单&#xff0c;使用了let内置函数 fun main() {//生成随机数可以使用java的方法//val magicNumber Random().nextInt(11)val ma…

如何确认linux的包管理器是yum还是apt,确认之后安装其他程序的时候就需要注意安装命令

打开终端 输入apt&#xff0c;下图中提示未找到命令&#xff0c;则基本上包管理工具就是用yum的 输入yum&#xff0c;我们看到有打印信息&#xff0c;则说明包管理工具是yum的&#xff0c;离线安装命令使用rpm