预防式编程——避免空值

文章目录

      • 1. 输入验证
      • 2. 使用可选类型(Optional Types)
      • 3. 非空断言
      • 4. 安全调用运算符
      • 5. 提供默认值
      • 6. 设计模式
      • 7. 文档说明
      • 8. 数据结构的选择
      • 9. 逻辑判断
      • 10. 构造函数和初始化
      • 11. 使用工具类
      • 12. 枚举类型
      • 13. 编码规范
      • 14. 测试
      • 15. 重构
      • 16. 教育与培训
    • 案例展示
      • 1. 定义书籍实体类
      • 2. 创建书籍管理服务
      • 3. 处理空值
      • 4. 使用 `Optional` 和默认值
      • 5. 异常处理和日志记录
        • 更新 `BookService`
      • 6. 客户端代码优化
        • 更新 `BookClient`
      • 7. 单元测试
        • 单元测试示例

避免空值(null 或 None 值)是预防式编程的一个重要方面,因为在很多编程语言中,对空值的不当处理可能会导致运行时错误,比如 NullPointerException 在 Java 中,或者在其他语言中类似的错误。以下是一些避免空值的技术:

1. 输入验证

  • 参数检查:在函数或方法开始时检查所有输入参数是否为空。如果检测到空值,则抛出异常或返回错误信息。
    public void process(String input) {if (input == null) {throw new IllegalArgumentException("Input cannot be null");}// 正常处理逻辑...
    }
    

2. 使用可选类型(Optional Types)

  • 在一些现代语言中,如 Java 8 及更高版本,引入了 Optional 类型来表示一个值可能存在也可能不存在的情况。这样可以强制调用者显式处理可能的空值。
    public Optional<String> findUserById(Long id) {// 模拟查找用户的过程User user = userRepository.findById(id);return Optional.ofNullable(user.getName());
    }public void printUserName(Long id) {findUserById(id).ifPresent(System.out::println);
    }
    

3. 非空断言

  • 在某些语言中,可以使用编译器特性来声明一个变量或参数不允许为空。例如,在 Kotlin 中,你可以声明一个非空类型,并在赋值时进行检查。
    fun printName(name: String) {  // name 不允许为 nullprintln(name)
    }
    

4. 安全调用运算符

  • 一些语言提供了安全调用运算符来避免空指针异常。例如,在 Kotlin 中,可以使用 ?. 运算符。
    val length: Int? = someObject?.name?.length
    

5. 提供默认值

  • 当遇到可能为空的值时,提供一个合理的默认值,这样即使对象为空也不会导致程序崩溃。
    String name = user.getName() != null ? user.getName() : "Unknown";
    

6. 设计模式

  • 工厂模式:可以通过工厂方法来创建对象,工厂负责确保永远不会返回空对象。
  • 单例模式:确保在整个应用中只有一个实例,避免多次初始化可能带来的空值问题。

7. 文档说明

  • 清晰地文档化每个方法的返回值是否可能为空,以及调用者应该如何处理这种情况。

8. 数据结构的选择

  • 使用容器类:在一些场景下,可以使用特定的容器类来代替直接使用基本类型或对象。例如,使用 List 而不是数组,可以避免处理空数组的问题。
    List<String> names = new ArrayList<>();
    // 如果需要一个空列表,可以直接使用空的 List 对象
    

9. 逻辑判断

  • 先判断再使用:在使用某个对象之前,先进行非空检查,确保对象存在后再执行相关操作。
    if (user != null && user.getName() != null) {System.out.println(user.getName());
    }
    

10. 构造函数和初始化

  • 确保对象初始化完整:在构造函数中确保所有的必要字段都被正确初始化,避免对象在创建后立即使用时出现空值。
    public class User {private String name;public User(String name) {this.name = Objects.requireNonNull(name, "Name cannot be null");}
    }
    

11. 使用工具类

  • 使用工具类处理空值:有些语言或框架提供了工具类来帮助处理空值,例如 Apache Commons Lang 的 StringUtilsObjects 类。
    String name = StringUtils.defaultIfEmpty(user.getName(), "Unknown");
    

12. 枚举类型

  • 使用枚举类型替代简单类型:在某些情况下,使用枚举类型可以明确表示某个值的存在与否,从而避免空值问题。
    enum Status {ACTIVE, INACTIVE, UNKNOWN
    }public Status getStatus() {// 返回一个枚举值而不是 nullreturn Status.UNKNOWN;
    }
    

13. 编码规范

  • 制定编码规范:团队内部可以制定关于如何处理空值的编码规范,确保所有人都遵循相同的规则,减少由于个人习惯不同而导致的问题。

14. 测试

  • 编写针对空值的测试用例:在单元测试中包括针对空值的测试,确保代码在遇到空值时能够正常工作或按预期抛出异常。
    @Test
    public void testProcessWithNullInput() {assertThrows(IllegalArgumentException.class, () -> process(null));
    }
    

15. 重构

  • 重构代码:定期检查代码中是否存在对空值的不当处理,并进行必要的重构,以提高代码质量。

16. 教育与培训

  • 加强团队培训:通过培训和分享会增强团队成员对空值问题的认识,提高他们处理空值的能力。

通过综合运用这些策略和技术,可以大大减少空值引发的问题,提高软件系统的健壮性和可靠性。

案例展示

让我们通过一个具体的案例来展示如何综合应用预防式编程技术来避免空值问题。假设我们正在开发一个图书管理系统,该系统需要处理书籍的信息,并能够根据书籍的 ID 查询书籍的详细信息。我们将通过以下几个步骤来展示如何避免空值:

1. 定义书籍实体类

首先定义一个 Book 实体类,包含书籍的基本信息:

public class Book {private final Long id;private final String title;private final String author;private final String isbn;public Book(Long id, String title, String author, String isbn) {this.id = Objects.requireNonNull(id, "ID cannot be null");this.title = Objects.requireNonNull(title, "Title cannot be null");this.author = Objects.requireNonNull(author, "Author cannot be null");this.isbn = Objects.requireNonNull(isbn, "ISBN cannot be null");}// Getterspublic Long getId() {return id;}public String getTitle() {return title;}public String getAuthor() {return author;}public String getIsbn() {return isbn;}
}

这里我们使用了 Objects.requireNonNull() 方法来确保所有传递给构造函数的参数都不为空。

2. 创建书籍管理服务

接下来定义一个 BookService 类来管理书籍的查询和其他业务逻辑:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class BookService {private final Map<Long, Book> books = new ConcurrentHashMap<>();public BookService() {// 初始化一些书籍数据books.put(1L, new Book(1L, "Clean Code", "Robert C. Martin", "978-0132350884"));books.put(2L, new Book(2L, "Design Patterns", "Erich Gamma", "978-0201633610"));}/*** 根据书籍 ID 查找书籍。** @param bookId 书籍的 ID* @return 书籍信息或空(如果找不到)*/public Book findBookById(Long bookId) {if (bookId == null) {throw new IllegalArgumentException("Book ID cannot be null");}return books.get(bookId);}
}

在这个服务中,我们使用了一个 ConcurrentHashMap 来存储书籍信息。findBookById 方法会根据提供的书籍 ID 查找书籍,并在找不到书籍时返回 null

3. 处理空值

为了更好地处理可能返回的 null 值,我们可以使用 Java 8 引入的 Optional 类型来改进我们的服务:

public Optional<Book> findBookById(Long bookId) {if (bookId == null) {throw new IllegalArgumentException("Book ID cannot be null");}return Optional.ofNullable(books.get(bookId));
}

现在,findBookById 方法返回一个 Optional<Book> 对象,即使没有找到对应的书籍,也会返回一个表示没有值的 Optional.empty()

4. 使用 Optional 和默认值

最后,我们来看一下如何在客户端代码中安全地使用这个服务:

public class BookClient {private final BookService bookService = new BookService();public void displayBookInfo(Long bookId) {Optional<Book> book = bookService.findBookById(bookId);book.ifPresentOrElse(b -> System.out.printf("Found book: %s by %s\n", b.getTitle(), b.getAuthor()),() -> System.out.println("No book found with the given ID."));}public static void main(String[] args) {BookClient client = new BookClient();client.displayBookInfo(1L);  // 应该打印出书籍信息client.displayBookInfo(3L);  // 应该打印出未找到的信息}
}

在这里,我们使用了 OptionalifPresentOrElse 方法来处理两种情况:找到书籍时打印书籍信息,未找到书籍时打印提示信息。

通过这种方式,我们不仅避免了空值带来的运行时错误,还提高了代码的可读性和可维护性。

我们继续扩展这个案例,确保更多的预防式编程实践得到应用。在实际应用中,我们还需要考虑更多的场景和细节,比如异常处理、日志记录等。下面我们将继续完善这个图书管理系统的服务端和客户端代码。

5. 异常处理和日志记录

在实际应用中,我们可能需要更细致地处理异常情况,并记录相关的日志信息,以便于后续的调试和监控。

更新 BookService

BookService 中添加日志记录,并改进异常处理:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;public class BookService {private static final Logger LOGGER = Logger.getLogger(BookService.class.getName());private final Map<Long, Book> books = new ConcurrentHashMap<>();public BookService() {// 初始化一些书籍数据books.put(1L, new Book(1L, "Clean Code", "Robert C. Martin", "978-0132350884"));books.put(2L, new Book(2L, "Design Patterns", "Erich Gamma", "978-0201633610"));}/*** 根据书籍 ID 查找书籍。** @param bookId 书籍的 ID* @return 包含书籍信息的 Optional 或空(如果找不到)*/public Optional<Book> findBookById(Long bookId) {if (bookId == null) {throw new IllegalArgumentException("Book ID cannot be null");}Book book = books.get(bookId);if (book == null) {LOGGER.log(Level.INFO, "Book not found for ID: " + bookId);}return Optional.ofNullable(book);}
}

这里我们引入了日志记录,当找不到书籍时,会在日志中记录相关信息。

6. 客户端代码优化

在客户端代码中,我们可以增加更多的健壮性处理,比如重试机制、异常捕获等。

更新 BookClient
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;public class BookClient {private static final Logger LOGGER = Logger.getLogger(BookClient.class.getName());private final BookService bookService = new BookService();public void displayBookInfo(Long bookId) {try {Optional<Book> book = bookService.findBookById(bookId);book.ifPresentOrElse(b -> System.out.printf("Found book: %s by %s\n", b.getTitle(), b.getAuthor()),() -> System.out.println("No book found with the given ID."));} catch (IllegalArgumentException e) {LOGGER.log(Level.SEVERE, "Invalid input detected: " + e.getMessage(), e);System.err.println("Error: " + e.getMessage());}}public static void main(String[] args) {BookClient client = new BookClient();client.displayBookInfo(1L);  // 应该打印出书籍信息client.displayBookInfo(3L);  // 应该打印出未找到的信息client.displayBookInfo(null);  // 应该打印出错误信息}
}

这里我们增加了对 IllegalArgumentException 的捕获,并记录了详细的错误信息。

7. 单元测试

为了确保我们的代码在各种情况下都能正常工作,我们需要编写单元测试来覆盖不同的场景,包括边界条件和异常情况。

单元测试示例

我们可以使用 JUnit 5 和 Mockito 来编写测试:

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;class BookServiceTest {@Testvoid shouldFindBookById() {// 假设的书籍数据Book expectedBook = new Book(1L, "Clean Code", "Robert C. Martin", "978-0132350884");// 模拟 BookServiceMap<Long, Book> books = Mockito.mock(Map.class);Mockito.when(books.get(1L)).thenReturn(expectedBook);BookService bookService = new BookService();bookService.books = books;  // 替换原有的 map// 断言assertEquals(Optional.of(expectedBook), bookService.findBookById(1L));}@Testvoid shouldThrowExceptionWhenBookIdIsNull() {BookService bookService = new BookService();assertThrows(IllegalArgumentException.class, () -> bookService.findBookById(null));}@Testvoid shouldReturnEmptyOptionalWhenBookNotFound() {BookService bookService = new BookService();assertEquals(Optional.empty(), bookService.findBookById(3L));}
}

通过这些测试,我们可以确保 BookService 在面对不同情况时的表现符合预期。

通过这些改进,我们不仅增强了代码的健壮性,还确保了在遇到错误时能够有条不紊地处理,并且通过日志记录和单元测试进一步提高了系统的可靠性和可维护性。

😍😍 海量H5小游戏、微信小游戏、Web casualgame源码😍😍
😍😍试玩地址: https://www.bojiogame.sg😍😍
😍看上哪一款,需要源码的csdn私信我😍

————————————————

​最后我们放松一下眼睛
在这里插入图片描述

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

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

相关文章

[Python学习日记-14] Python中基础语法的补充(变量增删改的过程、垃圾回收机制、变量指向关系、身份运算和None)

[Python学习日记-14] Python中基础语法的补充 变量增删改的过程 变量的指向关系 垃圾回收机制 身份运算和None 三元运算 变量增删改的过程 一、增 现在我们假设要创建一个变量名为 name 并且我们要赋它一个值“Jove”&#xff0c;那我们很自然会想到下面的代码 name &q…

使用Python本地搭建http.server文件共享服务并实现公网环境远程访问——“cpolar内网穿透”

前言 本文主要介绍如何在Windows系统电脑上使用python这样的简单程序语言&#xff0c;在自己的电脑上搭建一个共享文件服务器&#xff0c;并通过cpolar创建的公网地址&#xff0c;打造一个可以随时随地远程访问的私人云盘。 数据共享作为和连接作为互联网的基础应用&#xff…

011. Oracle-约束

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

【Web】XGCTF 西瓜杯 超详细题解

目录 CodeInject tpdoor easy_polluted Ezzz_php CodeInject eval里打代码注入 11);system("tac /0*");// tpdoor 可以传参isCache给../../config/route.php写入$config[request_cache_key] 打的是CheckRequestCache中间件解析的漏洞 think\middleware\Ch…

得物APP助力释放首发经济新活力,解锁年轻潮流密码

在消费升级与高质量发展的时代背景下&#xff0c;我国首发经济正以前所未有的活力蓬勃发展&#xff0c;成为推动市场繁荣、满足个性化消费需求的重要力量。首发&#xff0c;即产品首次在市场亮相&#xff0c;往往代表着最新的设计理念、最尖端的科技应用以及最前沿的潮流趋势。…

一文读懂数字化生态平台的多元功能!

在这个数字化飞速发展的时代&#xff0c;有个话题不得不提 —— 数字化生态平台的多元功能。 ​ 资源整合&#xff1a;凝聚各方力量 在当下&#xff0c;数字化生态平台就像一个大管家&#xff0c;能把分散在不同地方、不同主体的各类资源高效整合起来。商业领域里&#xff0c;…

STM32-HAL库开发快速入门

注:本文主要记录一下STM32CubeMX软件的使用流程,记录内容以STM32外设&#xff08;中断、I2C、USART、SPI等配置&#xff09;在STM32CubeMX中的设置为主&#xff0c;对驱动代码编写不做记录&#xff0c;所以阅读本文最好有标准库开发经验。除第2节外&#xff0c;使用的都是韦东山…

JAVA智能管理高效运营自营商城系统

智能管理&#xff0c;高效运营 —— 自营商城系统的魅力揭秘&#x1f680; &#x1f4c8; 开篇&#xff1a;告别繁琐&#xff0c;拥抱智能管理 你还在为商城运营的繁琐流程头疼吗&#xff1f;&#x1f92f; 还在为数据分析的复杂性而烦恼吗&#xff1f;&#x1f914; 那就让我…

IPv6理论基础

IPV6 地址介绍 地址标识方法 格式 : XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX&#xff0c;128bit&#xff0c;8个16位&#xff0c;16进制 X表示十六进制的一个字符&#xff0c;可以是0 - 9,A -F 字母大小写不敏感&#xff0c;最终设备上显示均为大写 一个地址由8个字段构成…

学习threejs,创建立方体,并执行旋转动画

文章目录 一、前言二、代码示例三、总结 一、前言 本文基于threejs&#xff0c;实现立方体的创建&#xff0c;并加入立方体旋转动画 二、代码示例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>l…

【动态规划】【完全背包】力扣322. 零钱兑换

给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。 你可以认为每种硬币的数量是无限的。 示…

【ACM出版,录用检索快】2024年第四届工商管理与数据科学国际学术会议 (BADS 2024,10月25-27)

2024年第四届工商管理与数据科学国际学术会议(BADS 2024)将于2024年10月25-27日在中国重庆召开&#xff0c;大会由喀什大学支持。 在当今全球化与数字化迅速发展的时代&#xff0c;工商管理与数据科学作为推动经济增长和技术进步的重要力量&#xff0c;正以前所未有的速度交叉融…

使用肘部法则确定K-Means中的k值

一 肘部法则 在K-means算法中&#xff0c;对于确定K&#xff08;簇的数目&#xff09;&#xff0c;我们经常使用肘部法则。 肘部法则是一种用于确定在k均值聚类算法中使用的质心数&#xff08;k&#xff09;的技术。 在这种方法中&#xff0c;为了确定k值&#xff0c;我们连续…

二十三种模式之原型模式(类比制作陶器更好理解一些)

1. 设计模式的分类 创建型模式(五种)&#xff1a;工厂方法模式、单例模式、抽象工厂模式、原型模式、建造者模式。 结构型模式(七种)&#xff1a;适配器模式、代理模式、装饰器模式、桥接模式、外观模式、享元模式、组合模式。 行为型模式(十一种)&#xff1a;状态模式、模板方…

刚开始学精益六西格玛管理方法?这份指南建议收藏

精益六西格玛管理方法&#xff0c;作为两大管理哲学的完美结合&#xff0c;正逐渐成为众多企业转型升级的利器。对于刚开始接触这一领域的你来说&#xff0c;掌握精益六西格玛管理的精髓并有效应用于实践中&#xff0c;无疑是一项既具挑战性又极具价值的任务。本文&#xff0c;…

应用连接错误,初始化mysql数据库恢复---惜分飞

有人在部署一个新网站的时候,写错了配置信息,直接导致原有数据库被清掉,并创建了新库和写入了数据(其实本质就是drop table恢复) 登录操作系统查看,发现数据库文件在根分区,创建了新库,写入了数据之外,还有几个G的binlog.全部恢复不太可能,最后客户决定需要恢复的2个核心表数…

.NET 一款在线解密Web.config的脚本

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

官网下载easyx压缩包,如何在devc++配置easyx

视频教程 官网下载easyx压缩包&#xff0c;如何在devc配置easyx EasyX Graphics Library for C 安装指南 1. 访问官网 官网 2. 下载 EasyX 在官网上找到下载区域&#xff0c;点击下载按钮以获取 EasyX 安装包。 3. 访问更多下载选项 点击页面上的“more”链接&#xff0…

Django日志

【图书介绍】《Django 5企业级Web应用开发实战&#xff08;视频教学版&#xff09;》_django 5企业级web应用开发实战(视频教学版)-CSDN博客 《Django 5企业级Web应用开发实战&#xff08;视频教学版&#xff09;》(王金柱)【摘要 书评 试读】- 京东图书 (jd.com) Django 5框…

uniapp 微信小程序自定义tabbar层级低于canvas解决方案

示例代码&#xff1a; <template><cover-view class"tab"><cover-view class"navView" tap"switc(/pages/index/index)"><cover-image :src"tabname index?/static/tabbar/overide-sel.png:/static/tabbar/overide…