当前位置: 首页 > backend >正文

mit6.031 2023spring 软件构造 笔记 Specification

为什么需要规范

当程序失败时,很难确定错误在哪里。 代码中的精确规范可以让您将责任分担给代码片段,而不是人。

有助于使模块更易于理解,就像黑匣子外部的标签一样。

拥有规范可以让你了解模块的作用,而无需阅读代码。

在这里插入图片描述

规范的结构 Specification structure

  • 函数签名,给出名称、参数类型和返回类型
  • require 子句,描述对参数的其他限制
  • effects 子句,描述函数的返回值、异常和其他效果

TypeScript中的规范

一些语言(尤其是 Eiffel)将前置条件和后置条件作为语言的基本组成部分,作为运行时系统(甚至编译器)可以自动检查的表达式,以强制执行客户端和实现者之间的契约。
TypeScript 并没有走得这么远,但它的静态类型声明实际上是函数的前置条件和后置条件的一部分,这是由编译器自动检查和强制执行的部分。 契约的其余部分——我们不能写成类型的部分——必须在函数之前的注释中进行描述,并且通常依赖于人类来检查和保证它。

// 函数签名:明确规定了参数 a 和 b 必须是数字,返回值也必须是数字。
// TypeScript 编译器会确保:
// 1. 调用者传入的必须是两个 number 类型的数据。
// 2. 函数实现者必须返回一个 number 类型的数据。
// 如果违反这些,代码在编译阶段就会报错,无法正常运行。
function average(a: number, b: number): number {return (a + b) / 2;
}// 合法的调用(编译器通过)
let result: number = average(5, 10); // result 为 7.5// 非法的调用(编译器报错,无法运行)
// average("5", "10"); // 错误:类型“string”的参数不能赋给类型“number”的参数。

一个常见的约定,最初是为 Java 设计的,但现在也被 ts 和 js 使用,是在函数之前放置一个文档注释,其中:

  • 参数由@param子句描述
  • 结果由 @returns 子句描述

What a specification may talk about

函数的规范可以谈论函数的参数和返回值,但永远不应该谈论函数的局部变量或函数类的私有字段。 您应该考虑该实现对规范的读者不可见。
就客户端而言,它位于防火墙后面。

避免空值

空值既麻烦又不安全,以至于好的编程会试图避免它们。
当 ts 启用了严格的 null 检查时,静态类型检查器保证这一点:

let word: string = null;  // compile error when strict null checking is on
let words: Array<string> = [ null ]; // compile error when strict null checking is on

谷歌在其核心Java库Guava中针对null值的使用进行了讨论,该项目说明:

  1. null是”万恶之源“
  • “粗心使用 null 会导致各种惊人的bug”: 这是因为 null 本质上是一个“空值”或“无值”的表示。如果你试图在一个期望有实际对象的地方使用 null(例如,调用 null.toString()),程序会立即抛出 NullPointerException (NPE),导致崩溃。
  • “95%的集合本不应包含任何null值”: 这是一个非常重要的观察。在绝大多数业务逻辑中,我们往集合(如 List, Map, Set)里放的都是有意义的实际对象。允许 null 被放入集合,相当于在数据结构中埋下了一颗“地雷”,未来任何遍历或使用这个集合的代码都可能意外踩到它而崩溃。
  • “快速失败(Fail Fast*)比静默接受更有帮助”
    • “静默接受”: 指代码不做检查,允许 null 被存入集合或传递给方法。这很危险,因为错误不会在源头被发现,而是会传播到后续代码中,在某个不可预知的地方爆发,使得调试变得极其困难。
    • “快速失败”: 指立即报错。如果一个方法或集合明确规定不允许 null,那么一旦传入 null,它就应该立刻抛出异常(如 NullPointerExceptionIllegalArgumentException)。
    • 为什么“快速失败”更好? 因为它让错误在发生的地点就立刻暴露出来,而不是隐藏起来直到后续某个不确定的时刻。这极大地简化了调试过程,开发者能马上定位到是哪个调用者传入了非法的 null 值。
  1. null 令人不快的歧义性”: 这是 null 的另一个致命缺点。当你看到一个方法返回 null 时,你无法确切知道这个 null 到底想表达什么语义

    经典例子:Map.get(key)

    • 这个调用返回 null 可能有两种完全不同的含义:
      1. 键(key)不存在于映射(Map)中。 (这是一种“失败”或“未找到”的信号)
      2. 键(key)存在,但它对应的值(value)本身就是 null (这是一种“成功找到,但找到的值是 null”的信号)
    • 作为API的调用者,你根本无法区分这两种情况!你必须额外调用 map.containsKey(key) 来确认,这很麻烦,而且容易忘记。

    “Null可以意味着失败、成功,几乎任何事”: 在不同的API设计里,null 被用来代表各种意思:“未初始化”、“错误”、“空结果”、“未找到”等等。没有统一的规范,导致代码可读性急剧下降。

    “使用 null 以外的其他东西可以使你的意图清晰”: 这是Guava(以及现代Java、其他语言)推荐的解决方案。使用明确、无歧义的表达方式来替代 null

    • Guava/Java 8+ 的解决方案:Optional<T>
      • 如果一个方法可能没有返回值,它应该声明为返回 Optional<String>,而不是 String
      • Optional.of("Hello") 表示肯定有一个值(“Hello”)。
      • Optional.absent() (Guava) 或 Optional.empty() (Java 8) 表示肯定没有值
      • 这样,调用者看到返回类型就知道可能需要处理空值的情况,并且 null 不再被用作“无值”的信号,歧义性就消失了。
http://www.xdnf.cn/news/19720.html

相关文章:

  • 【LeetCode 热题 100】1143. 最长公共子序列——(解法二)递推
  • 2025 大学生职业准备清单:从数据到财会,这些核心证书值得考
  • 【IO】多进程编程课后练习
  • 单多行文本溢出
  • Selenium核心技巧:元素定位与等待策略
  • ArkUI核心功能组件使用
  • 【线段树】3525. 求出数组的 X 值 II|2645
  • Spring 事务原理解析:AOP 的一次完美落地
  • 深度学习——基于卷积神经网络实现食物图像分类【4】(使用最优模型)
  • 广度优先搜索(BFS, Breadth-First Search)
  • 数字化转型的六大天问:你的项目为何失败
  • 数据开发工作了一年,准备跳槽,回顾一些面试常见问题,大数据面试题汇总与答案分享
  • 【3D打印】3D打印机首次使用心得
  • 2025最新“Java 面试八股文 + 各大厂的面试真题”限时开源
  • 人工智能助力流感疫苗选择:MIT 团队推出 VaxSeer 系统
  • Understanding the Flap T in American English
  • 开源企业级快速开发平台(JeecgBoot)
  • Python闭包机制:原理、应用与安全防护
  • 【Doris入门】Doris数据表模型:聚合模型(Aggregate Key Model)详解
  • java-设计模式-4-创建型模式-工厂
  • 【52页PPT】服务业数字化转型如何做(附下载方式)
  • Ubuntu 用户和用户组
  • X86、X64 与 ARM:架构的剖析与比较
  • webpack性能优化指南
  • MacOS - 记录MacOS发烫的好几天 - 幕后黑手竟然是
  • 神经网络|(十八)概率论基础知识-伽马函数溯源-阶乘的积分表达式
  • k8s常用命令
  • 对矩阵行化简操作几何含义的理解
  • HDI是什么?与普通线路板有何区别?优势在哪?
  • 嵌入式git分支管理策略