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

CH10_简化条件逻辑

分解条件表达式(Decompose Conditional)

在这里插入图片描述

if (!aDate.isBefore(plan.summerStart) && !aDate.isAfter(plan.summerEnd))charge = quantity * plan.summerRate;
elsecharge = quantity * plan.regularRate + plan.regularServiceCharge;
if (summer())charge = summerCharge();
elsecharge = regularCharge();

动机

程序之中,复杂的条件逻辑是最常导致复杂度上升的地点之一。大型函数本身就会使代码的可读性下降,而条件逻辑则会使代码更难阅读。

将复杂的条件逻辑分解为多个独立的函数,根据每个小块代码的用途,为分解而得的新函数命名,并将原函数中对应的代码改为调用新函数,从而更清楚地表达自己的意图。对于条件逻辑,将每个分支条件分解成新函数还可以带来更多好处:可以突出条件逻辑,更清楚地表明每个分支的作用,并且突出每个分支的原因。

分解条件表达式其实只是提炼函数(106)的一个应用场景。

做法

  • 对条件判断和每个条件分支分别运用提炼函数(106)手法。

合并条件表达式(Consolidate Conditional Expression)

在这里插入图片描述

if (anEmployee.seniority < 2) return 0;
if (anEmployee.monthsDisabled > 12) return 0;
if (anEmployee.isPartTime) return 0;
if (isNotEligibleForDisability()) return 0;
function isNotEligibleForDisability() {return ((anEmployee.seniority < 2)|| (anEmployee.monthsDisabled > 12)|| (anEmployee.isPartTime));
}

动机

检查条件各不相同,最终行为却一致。如果发现这种情况,就应该使用“逻辑或”和“逻辑与”将它们合并为一个条件表达式。

之所以要合并条件代码,有两个重要原因。首先,合并后的条件代码会表述“实际上只有一次条件检查,只不过有多个并列条件需要检查而已”,从而使这一次检查的用意更清晰。其次,这项重构往往可以为使用提炼函数(106)做好准备。

条件语句的合并理由也同时指出了不要合并的理由:如果认为这些检查的确彼此独立的确不应该被视为同一次检查。

做法

  • 的确定这些条件表达式都没有副作用。
  • 使用适当的逻辑运算符,将两个相关条件表达式合并为一个。
  • 测试。
  • 重复前面的合并过程,直到所有相关的条件表达式都合并到一起。
  • 可以考虑对合并后的表达式实施提炼函数(106)。

以卫语句取代嵌套条件表达式(Replace Nested Conditional with Guard Clauses)

在这里插入图片描述

function getPayAmount() {let result;if (isDead)result = deadAmount();else {if (isSeparated)result = separatedAmount();else {if (isRetired)result = retiredAmount();elseresult = normalPayAmount();}}return result;
}
function getPayAmount() {if (isDead) return deadAmount();if (isSeparated) return separatedAmount();if (isRetired) return retiredAmount();return normalPayAmount();
}

动机

条件表达式通常有两种风格:第一种风格是:两个条件分支都属于正常行为。第二种风格则是:只有一个条件分支是正常行为,另一个分支则是异常的情况。

如果两条分支都是正常行为,就应该使用形如if…else…的条件表达式;如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。这样的单独检查常常被称为“卫语句”(guard clauses)。

以卫语句取代嵌套条件表达式的精髓就是:给某一条分支以特别的重视。如果使用if-then-else结构,对if分支和else分支的重视是同等的。这样的代码结构传递给阅读者的消息就是:各个分支有同样的重要性。卫语句就不同了,它告诉阅读者:“这种情况不是本函数的核心逻辑所关心的,如果它真发生了,请做一些必要的整理工作,然后退出。”

以前的编程语言都强调“每个函数只能有一个入口和一个出口”的观念,现今的编程语言都会强制保证每个函数只有一个入口,至于“单一出口”规则,其实不是那么有用,保持代码清晰才是最关键的。如果单一出口能使这个函数更清楚易读,那么就使用单一出口;否则就不必这么做。

做法

  • 选中最外层需要被替换的条件逻辑,将其替换为卫语句。
  • 测试。
  • 有需要的话,重复上述步骤。
  • 如果所有卫语句都引发同样的结果,可以使用合并条件表达式(263)合并之。

以多态取代条件表达式(Replace Conditional with Polymorphism)

在这里插入图片描述

switch (bird.type) {case 'EuropeanSwallow':return "average";case 'AfricanSwallow':return (bird.numberOfCoconuts > 2) ? "tired" : "average";case 'NorwegianBlueParrot':return (bird.voltage > 100) ? "scorched" : "beautiful";default:return "unknown";
}
class EuropeanSwallow {get plumage() {return "average";}
}
class AfricanSwallow {get plumage() {return (this.numberOfCoconuts > 2) ? "tired" : "average";}
}
class NorwegianBlueParrot {get plumage() {return (this.voltage > 100) ? "scorched" : "beautiful";}
}

动机

复杂的条件逻辑是编程中最难理解的东西之一。很多时候,可以将条件逻辑拆分到不同的场景(或者叫高阶用例),从而拆解复杂的条件逻辑。这种拆分有时用条件逻辑本身的结构就足以表达,但使用类和多态能把逻辑的拆分表述得更清晰。

常用场景:可以针对switch语句中的每种分支逻辑创建一个类,用多态来承载各个类型特有的行为,从而去除重复的分支逻辑。

另一种情况:有一个基础逻辑,在其上又有一些变体。基础逻辑可能是最常用的,也可能是最简单的。把基础逻辑放进超类,这样可以首先理解这部分逻辑,暂时不管各种变体,然后可以把每种变体逻辑单独放进一个子类,其中的代码着重强调与基础逻辑的差异。

多态是面向对象编程的关键特性之一。跟其他一切有用的特性一样,它也很容易被滥用。大部分条件逻辑用基本的条件语句——if/else和switch/case就能应付,并不需要劳师动众地引入多态。

做法

  • 如果现有的类尚不具备多态行为,就用工厂函数创建之,令工厂函数返回恰当的对象实例。
  • 在调用方代码中使用工厂函数获得对象实例。
  • 将带有条件逻辑的函数移到超类中。
  • 任选一个子类,在其中建立一个函数,使之覆写超类中容容纳条件表达式的那个函数。将该子类相关的条件表达式分支复制到新函数中,并对它进行适当调整。
  • 重复上述过程,处理其他条件分支。
  • 在超类函数中保留默认情况的逻辑。或者,如果超类应该是抽象的,就把该函数声明为abstract,或在其中直接抛出异常,表明计算责任都在子类中。

引入特例(Introduce Special Case)

曾用名:引入Null对象(Introduce Null Object)

在这里插入图片描述

if(aCustomer === "unknown") customerName="occupant";
class UnknownCustomer {get name() {return "occupant";}
}

动机

一种常见的重复代码是这种情况:一个数据结构的使用者都在检查某个特殊的值,并且当这个特殊值出现时所做的处理也都相同。

处理这种情况的一个好办法是使用“特例”(SpecialCase)模式:创建一个特例元素,用以表达对这种特例的共用行为的处理。

特例有几种表现形式。如果只需要从这个对象读取数据,可以提供一个字面量对象(literal object),其中所有的值都是预先填充好的。如果除简单的数值之外还需要更多的行为,就需要创建一个特殊对象,其中包含所有共用行为所对应的函数。特例对象可以由一个封装类来返回,也可以通过变换插入一个数据结构。

做法

从一个作为容器的数据结构(或者类)开始,其中包含一个属性,该属性就是要重构的目标。容器的客户端每次使用这个属性时,都需要将其与某个特例值做比对。然后把这个特例值替换为代表这种特例情况的类或数据结构。

  • 给重构目标添加检查特例的属性,令其返回false。
  • 创建一个特例对象,其中只有检查特例的属性,返回true。
  • 对“与特例值做比对”的代码运用提炼函数(106),确保所有客户端都使用这个新函数,而不再直接做特例值的比对。
  • 将新的特例对象引入代码中,可以从函数调用中返回,也可以在变换函数中生成。
  • 修改特例比对函数的主体,在其中直接使用检查特例的属性。
  • 测试。
  • 使用函数组合成类(144)或函数组合成变换(149),把通用的特例处理逻辑都搬移到新建的特例对象中。
  • 对特例比对函数使用内联函数(115),将其内联到仍然需要的地方。

引入断言(Introduce Assertion)

在这里插入图片描述

if (this.discountRate)base = base - (this.discountRate * base);
assert(this.discountRate>= 0);
if (this.discountRate)base = base - (this.discountRate * base);

动机

常常会有这样一段代码:只有当某个条件为真时,该段代码才能正常运行。这样的假设通常并没有在代码中明确表现出来,必须阅读整个算法才能看出。

断言是一个条件表达式,应该总是为真。如果它失败,表示程序员犯了错误。断言的失败不应该被系统任何地方捕捉。整个程序的行为在有没有断言出现的时候都应该完全一样。实际上,有些编程语言中的断言可以在编译期用一个开关完全禁用掉。

做法

  • 如果发现代码假设某个条件始终为真,就加入一个断言明确说明这种情况。

因为断言应该不会对系统运行造成任何影响,所以“加入断言”永远都应该是行为保持的。

相关文章:

CH10_简化条件逻辑

分解条件表达式&#xff08;Decompose Conditional&#xff09; if (!aDate.isBefore(plan.summerStart) && !aDate.isAfter(plan.summerEnd))charge quantity * plan.summerRate; elsecharge quantity * plan.regularRate plan.regularServiceCharge;if (summer())…...

nn.LayerNorm解释

这个是层归一化。我们输入一个参数&#xff0c;这个参数就必须与最后一个维度对应。但是我们也可以输入多个维度&#xff0c;但是必须从后向前对应。 import torch import torch.nn as nna torch.rand((100,5)) c nn.LayerNorm([5]) print(c(a).shape)a torch.rand((100,5,…...

Springboot搭建微服务案例之Eureka注册中心

一、父工程依赖管理 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org…...

【MySQL】用户管理权限控制

文章目录 前言一. 用户管理1. 创建用户2. 删除用户3. 修改用户密码 二. 权限控制1. 用户授权2. 查看权限3. 回收权限 结束语 前言 MySQL的数据其实也以文件形式保存&#xff0c;而登录信息同样保存在文件中 MySQL的数据在Linux下默认路径是/var/lib/mysql 登录MySQL同样也可以…...

若依框架前后端分离版服务器部署,前端nginx的配置

server {listen 80;server_name 120.46.177.184;index index.php index.html index.htm default.php default.htm default.html;root /www/wwwroot/qilaike-vue/dist;#SSL-START SSL相关配置&#xff0c;请勿删除或修改下一行带注释的404规则#error_page 404/404.html;#SSL-END…...

基于单片机的滚筒洗衣机智能控制系统设计

收藏和点赞&#xff0c;您的关注是我创作的动力 文章目录 概要 一、系统整体设计方案2.1控制系统的功能2.2设计的主要内容 二、硬件设计3.1 控制系统整体框图3.2 电源电路 三 软件设计主程序设计仿真设计 四、 结论 概要 因此我们需要一个完善的智能系统来设计一个全自动滚筒洗…...

简述多模态学习中,对齐、融合和表示

在多模态学习中&#xff0c;对齐、融合和表示是三个核心概念&#xff0c;它们相互关联&#xff0c;共同支持多模态数据的处理和分析。 对齐&#xff08;Alignment&#xff09; 对齐是多模态学习中的一个关键步骤&#xff0c;它涉及到如何在不同的数据模态之间发现和建立对应关…...

Kotlin 进阶函数式编程技巧

Kotlin 进阶函数式编程技巧 Kotlin 简介 软件开发环境不断变化&#xff0c;要求开发人员不仅适应&#xff0c;更要进化。Kotlin 以其简洁的语法和强大的功能迅速成为许多人进化过程中的信赖伙伴。虽然 Kotlin 的初始吸引力可能是它的简洁语法和与 Java 的互操作性&#xff0c…...

操作系统——内存映射文件(王道视频p57)

1.总体概述&#xff1a; 2.传统文件访问方式&#xff1a; 我认为&#xff0c;这种方式最大的劣势在于&#xff0c;如果要对整个文件的不同部分进行多次操作的话&#xff0c;这样确实开销可能会大一些&#xff0c;而且程序员还要指定对应的“分块”载入到内存中 3.内存映射文件…...

王道p18 07.将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表。(c语言代码实现)

视频讲解在这&#xff1a;&#x1f447; p18 第7题 c语言代码实现王道数据结构课后代码题_哔哩哔哩_bilibili 本题代码如下 int merge(struct sqlist* A, struct sqlist* B, struct sqlist* C) {if (A->length B->length > C->length)//大于顺序表的最大长度r…...

2024最新mac电脑清理垃圾的软件有哪些?

mac电脑是许多人喜爱的电子产品&#xff0c;它拥有优美的设计、流畅的操作系统和强大的性能。但是&#xff0c;随着使用时间的增长&#xff0c;mac电脑也会积累一些不必要的垃圾文件&#xff0c;这些文件会占用宝贵的存储空间&#xff0c;影响电脑的运行速度和稳定性。因此&…...

2023年【山东省安全员C证】考试技巧及山东省安全员C证模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 山东省安全员C证考试技巧考前必练&#xff01;安全生产模拟考试一点通每个月更新山东省安全员C证模拟试题题目及答案&#xff01;多做几遍&#xff0c;其实通过山东省安全员C证模拟考试题很简单。 1、【多选题】《环境…...

2024最新免费的mac电脑清理垃圾的软件有哪些?

mac电脑是许多人喜爱的电子产品&#xff0c;它拥有优美的设计、流畅的操作系统和强大的性能。但是&#xff0c;随着使用时间的增长&#xff0c;mac电脑也会积累一些不必要的垃圾文件&#xff0c;这些文件会占用宝贵的存储空间&#xff0c;影响电脑的运行速度和稳定性。因此&…...

linux下sqlplus登录oracle显示问号处理办法

问题描述 昨天紧急通过rpm按安装方式给客户装了一台linux的19c数据库&#xff0c;操作系统是CentOs Stream release 9&#xff0c;过程不再回忆了… 今天应用发现sqlplus登入后部分显示问号&#xff1f;&#xff0c;需要处理下 原因分析&#xff1a; 很明显&#xff0c;这就是…...

Git 删除本地和远程分支

目录 删除本地和远程分支分支删除验证验证本地分支验证远程分支 开源项目微服务商城项目前后端分离项目 删除本地和远程分支 删除 youlai-mall 的 dev 本地和远程分支 # 删除本地 dev 分支&#xff08;注&#xff1a;一定要切换到dev之外的分支才能删除&#xff0c;否则报错&…...

Selenium元素定位之页面检测技巧

在进行web自动化测试的时候进行XPath或者CSS定位&#xff0c;需要检测页面元素定位是否正确&#xff0c;如果用脚本去检测&#xff0c;那么效率是极低的。 一般网上推选装额外的插件来实现页面元素定位检测 如&#xff1a;firebug。 其实F12开发者工具就能直接在页面上检测元…...

C# 文件 文件夹 解除占用

文件/文件夹 解除占用或直接删除。 编程语言&#xff1a;C# 这个就不用过多功能描述了。 注册windows 文件/文件夹 右键菜单。 文件夹解除占用&#xff1a;遍历文件夹所有文件&#xff0c;判断是否被占用&#xff0c;先解除文件占用&#xff0c;后解除文件夹占用&#xff0…...

数据库 存储引擎

存储引擎概念 在mysql当中数据库用不同的技术存储在文件中&#xff0c;每一种技术都是使用不同的存储引擎机制&#xff0c;索引技巧&#xff0c;锁定水平&#xff0c;以及最终提供的不同的功能和能力&#xff0c;这些就是我们说的存储引擎 主要功能 1mysql将数据存储在文件系…...

操作系统复习(2)进程管理

一、概述 1.1程序的顺序执行 一个具有独立功能的程序独占CPU运行&#xff0c;直至得到最终结果的过程称为程序的顺序执行。 程序的并发执行所表现出的特性说明两个问题 ⑴ 程序和计算机执行程序的活动不再一一对应 ⑵ 并发程序间存在相互制约关系&#xff08;要求共享信息&…...

通过51单片机控制28byj48步进电机按角度正反转旋转

一、前言 本项目基于STC89C52单片机&#xff0c;通过控制28BYJ-48步进电机实现按角度正反转旋转的功能。28BYJ-48步进电机是一种常用的电机&#xff0c;精准定位和高扭矩输出&#xff0c;适用于许多小型的自动化系统和机械装置。 在这个项目中&#xff0c;使用STC89C52单片机…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...