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

【Code】《代码整洁之道》笔记-Chapter12-迭进

第12章 迭进

12.1 通过迭进设计达到整洁目的

假使有4条简单的规则,跟着做就能帮助你创建优良的设计,会如何?假使遵循这些规则,你就能洞见代码的结构和设计,更能轻易地应用SRP和DIP之类的原则,便会如何?假使这4条规则有利于良好的设计“浮现”出来,又会如何?

我们中的许多人认为,Kent Beck关于简单设计的4条规则,对于创建具有良好设计的软件有着莫大的帮助。

据Kent所述,只要遵循以下规则,设计就能变得“简单”:

  • 运行所有测试;

  • 不可重复;

  • 表达了程序员的意图;

  • 尽可能减少类和方法的数量。

以上规则按其重要程度排列。

12.2 简单设计规则1:运行所有测试

设计必须制造出如预期一般工作的系统,这是首要因素。系统也许有一套绝佳设计,但如果缺乏验证系统是否真按预期那样工作的简单方法,那就无异于纸上谈兵。

全面测试并持续通过所有测试的系统,就是可测试的系统。这看似浅显,却很重要。不可测试的系统同样不可验证。不可验证的系统,绝不应部署。

幸运的是,只要系统可测试,就会导向保持类短小且目的单一的设计方案。遵循SRP的类,测试起来较为简单。测试编写得越多,就越能持续走向编写较易测试的代码。所以,确保系统完全可测试能帮助我们创建更好的设计。

紧耦合的代码难以编写测试。同样,编写测试越多,就越会遵循DIP之类的规则,从而越会使用依赖注入、接口和抽象等工具尽可能减少耦合,如此一来,设计就会有长足进步。

遵循有关编写测试并持续运行测试的简单、明确的规则,系统就会更贴近面向对象低耦合度、高内聚度的目标。编写测试将会引致更好的设计。

12.3 简单设计规则2~4:重构

有了测试,就能保持代码和类的整洁,方法就是递增式地重构代码。添加了几行代码后,就要暂停,琢磨一下变化了的设计。设计退步了吗?如果是,就要清理它,并且运行测试,保证没有破坏任何东西。测试消除了对清理代码就会破坏代码的恐惧。

在重构过程中,可以应用有关优秀软件设计的一切知识,提升内聚性,降低耦合度,切分关注面,模块化系统性关注面,缩小函数和类的尺寸,选用更好的名称,如此等等。这也是应用简单设计后3条规则的地方:消除重复,保证表达力,尽可能减少类和方法的数量。

12.4 不可重复

重复是拥有良好设计的系统的大敌。它代表着额外的工作、额外的风险和额外且不必要的复杂度。重复有多种表现。极其雷同的代码行当然是重复。类似的代码往往可以调整得更相似,这样就能更容易地进行重构。重复也有实现上的重复等其他一些形态,例如,在某个群集类中可能会有以下两个方法:

int size()  {}
boolean isEmpty()  {}

这两个方法可以分别实现。isEmpty方法跟踪一个布尔值,而size方法则跟踪一个计数器。或者,也可以通过在isEmpty方法中使用size方法来消除重复:

boolean isEmpty() {return 0 == size();
}

要想创建整洁的系统,需要有消除重复的意愿,即便对于短短几行也是如此。例如,以下代码:

 public void scaleToOneDimension(float desiredDimension, float imageDimension) {if (Math.abs(desiredDimension - imageDimension) < errorThreshold)return;float scalingFactor = desiredDimension / imageDimension;scalingFactor = (float)(Math.floor(scalingFactor * 100) * 0.01f);RenderedOp newImage = ImageUtilities.getScaledImage(image, scalingFactor, scalingFactor);image.dispose();System.gc();image = newImage;}public synchronized void rotate(int degrees) {RenderedOp newImage = ImageUtilities.getRotatedImage(image, degrees);image.dispose();System.gc();image = newImage;
}

要保持系统整洁,应该消除scaleToOneDimension方法和rotate方法里面的少量重复:

public void scaleToOneDimension(float desiredDimension, float imageDimension) {if (Math.abs(desiredDimension - imageDimension) < errorThreshold)return;float scalingFactor = desiredDimension / imageDimension;scalingFactor = (float)(Math.floor(scalingFactor * 100) * 0.01f);replaceImage(ImageUtilities.getScaledImage(image, scalingFactor, scalingFactor)); 
}public synchronized void rotate(int degrees) {replaceImage(ImageUtilities.getRotatedImage(image, degrees)); 
}private void replaceImage(RenderedOp newImage) {image.dispose();System.gc();image = newImage;
}

做了一点点共性抽取后,我们意识到已经违反了SRP原则。所以,可以把一个新方法分解到另外的类中,从而提升其可见性。团队中的其他成员也许会发现进一步抽象新方法的机会,并且在其他场景中复用之。“小规模复用”可大量降低系统复杂性。要想实现大规模复用,必须理解如何实现小规模复用。

模板方法模式(Template Method)是一种移除高层级重复的通用技巧。例如:

public class VacationPolicy {public void accrueUSDivisionVacation() {// code to calculate vacation based on hours worked to date// ...// code to ensure vacation meets US minimums// ...// code to apply vaction to payroll record// ...}public void accrueEUDivisionVacation() {// code to calculate vacation based on hours worked to date// ...// code to ensure vacation meets EU minimums// ...// code to apply vaction to payroll record// ...}}

除了计算法定最少数量假期的部分,accrueUSDivisionVacationaccrue-EuropeanDivisionVacation中有大量代码雷同。那部分的算法,依据员工类型而变。

可以通过应用模板方法模式来消除明显的重复。

abstract public class VacationPolicy {public void accrueVacation() {calculateBaseVacationHours(); alterForLegalMinimums(); applyToPayroll(); }private void calculateBaseVacationHours() { /* ... */ };abstract protected void alterForLegalMinimums();private void applyToPayroll() { /* ... */ };
}public class USVacationPolicy extends VacationPolicy {@Override protected void alterForLegalMinimums() {// US specific logic}
}public class EUVacationPolicy extends VacationPolicy {@Override protected void alterForLegalMinimums() {// EU specific logic}
}

子类填充了accrueVacation算法中的“空洞”,提供不重复的信息。

12.5 表达力

我们中的大多数人都经历过费解代码的纠缠。我们中的许多人自己就编写过费解的代码。写出自己能理解的代码很容易,因为在写这些代码时,我们正深入于要解决的问题中。代码的其他维护者不会那么深入,也就不易理解代码。

软件项目的主要成本在于长期的维护。为了在修改时尽量降低出现缺陷的可能性,很有必要理解系统是做什么的。当系统变得越来越复杂,开发者就需要越来越多的时间来理解它,而且也极有可能误解。所以,代码应当清晰地表达其作者的意图。作者把代码写得越清晰,其他人花在理解代码上的时间也就越少,从而减少缺陷,缩减维护成本。

可以通过选用好名称来表达。我们想要听到好类名和好函数名,而且在查看其权责时不会大吃一惊。

也可以通过保持函数和类尺寸短小来表达。短小的类和函数通常易于命名,易于编写,易于理解。

还可以通过采用标准命名法来表达。例如,设计模式很大程度上关乎沟通和表达。通过在实现这些模式的类的名称中采用标准模式名,如COMMAND或VISITOR,就能充分地向其他开发者描述你的设计。

编写良好的单元测试也具有表达性。测试的主要目的之一就是通过实例起到文档的作用。读到测试的人应该能很快理解某个类是做什么的。

不过,做到有表达力的最重要方式却是尝试。有太多时候,我们一旦写出能工作的代码,就转移到下一个问题上,而没有下足功夫调整代码,让后来者易于阅读。记住,下一位读代码的人最有可能是你自己。

所以,多少尊重一下你的手艺吧。花一点点时间在每个函数和类上。选用较好的名称,将大函数切分为小函数,时时照拂自己创建的东西。用心是最珍贵的资源。

12.6 尽可能少的类和方法

即便是消除重复、代码表达力和SRP等最基础的概念,也会被过度使用。为了保持类和函数短小,我们可能会造出太多的细小类和方法。所以这条规则也主张函数和类的数量要少。

类和方法的数量太多,有时是由毫无意义的教条主义导致的。例如,某个编码标准就坚称应当为每个类创建接口。也有开发者认为,字段和行为必须切分到数据类和行为类中。应该抵制这类教条,采用更实用的手段。

我们的目标是在保持函数和类短小的同时,保持整个系统短小精悍。不过要记住,这在关于简单设计的4条规则里面是优先级最低的一条。所以,尽管使类和函数的数量尽量少是很重要的,但更重要的却是测试、消除重复和表达力。

12.7 小结

有没有能替代经验的一套简单实践手段呢?当然不会有。另外,本章中写到的实践来自本书作者数十年经验的精练总结。遵循简单设计的实践手段,开发者不必经年学习就能掌握好的原则和模式。

相关文章:

【Code】《代码整洁之道》笔记-Chapter12-迭进

第12章 迭进 12.1 通过迭进设计达到整洁目的 假使有4条简单的规则&#xff0c;跟着做就能帮助你创建优良的设计&#xff0c;会如何&#xff1f;假使遵循这些规则&#xff0c;你就能洞见代码的结构和设计&#xff0c;更能轻易地应用SRP和DIP之类的原则&#xff0c;便会如何&…...

04--网络属性设置与多路复用

一、TCP可靠性分析 二、 scoket 属性设置 1、socket 属性设置表 NAMEgetsockopt, setsockopt - get and set options on sockets获取 和 设置 套接字属性 SYNOPSIS#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int getsockopt(int so…...

AI领域再突破,永洪科技荣获“2025人工智能+创新案例”奖

在2025年的今天&#xff0c;人工智能已从技术概念全面渗透至产业核心。中国作为全球AI技术应用的前沿阵地&#xff0c;正通过“人工智能”行动加速推进技术与实体经济深度融合。 这一背景下&#xff0c;永洪科技凭借其“国内某头部ICT人力资源板块GenAI项目”荣获“2025全国企业…...

基于疾风大模型的新能源储能优化系统:方法、实现与案例分析

一、引言 随着可再生能源渗透率不断提高,储能系统在电力系统中的重要性日益凸显。传统储能控制方法主要基于规则策略和简单优化算法,难以应对高比例新能源场景下的复杂决策需求。本文将详细介绍如何利用疾风大模型(Gale Model)构建智能化的新能源储能优化系统,包含核心方…...

菊风RTC 2.0 开发者文档正式发布,解锁音视频新体验!

重磅发布&#xff01; 开发者们&#xff0c;菊风实时音视频2.0文档已正式发布上线&#xff0c;为您提供更清晰、更高效的开发支持&#xff01;让菊风实时音视频2.0为您的音视频应用加速~ 菊风实时音视频2.0聚焦性能升级、体验升级、录制服务升级&#xff0c;助力视频通话、语…...

12c补丁滚动升级

12c打补丁前置检查 备份文件&#xff0c;可以不做&#xff0c;因为文件可能很大&#xff0c;如果可以备份整个安装文件。 1.check grid&#xff1a; % /u01/app/12.1.0/grid/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -phBaseDir /home/software/27010872/2691…...

OpenCv高阶(一)——图像金字塔(上采样、下采样)

目录 图像金字塔 一、上下采样原理 1、向下取样 2、向上采样 3、图像金字塔的作用 二、案例实现 1、高斯下采样 2、高斯金字塔中的上采样 3、对下采样的结果做上采样&#xff0c;图像变模糊&#xff0c;无法复原 4、拉普拉斯金字塔&#xff08;图片复原&#xff09; 图…...

LEARNING DYNAMICS OF LLM FINETUNING【论文阅读笔记】

LEARNING DYNAMICS OF LLM FINETUNING 一句话总结 作者将LLM的学习动力机制拆解成AKG三项&#xff0c;并分别观察了SFT和DPO训练过程中​​正梯度信号​​和​​负梯度信号​​的变化及其带来的影响&#xff0c;并得到以下结论&#xff1a; ​​SFT通过梯度相似性间接提升无关…...

数据集 | 沥青路面缺陷目标检测

文章目录 一、数据集概述1. 行业痛点与数据集价值2. 数据集技术规格 二、样本类别详解1. 裂缝 (Crack)2. 裂缝修补 (Crack Repair)3. 坑洞 (Pothole)4. 坑洞修补 (Pothole Repair)5. 井盖 (Manhole Cover)6. 其他 (Other) 三、标注工具四、下载地址 一、数据集概述 1. 行业痛点…...

AllData数据中台升级发布 | 支持K8S数据平台2.0版本

&#x1f525;&#x1f525; AllData大数据产品是可定义数据中台&#xff0c;以数据平台为底座&#xff0c;以数据中台为桥梁&#xff0c;以机器学习平台为中层框架&#xff0c;以大模型应用为上游产品&#xff0c;提供全链路数字化解决方案。 ✨杭州奥零数据科技官网&#xf…...

第二十二: go与k8s、docker相关编写dockerfile

实战演示k8s部署go服务&#xff0c;实现滚动更新、重新创建、蓝绿部署、金丝雀发布-CSDN博客 go 编写k8s命令&#xff1a; 怎么在go语言中编写k8s命令 • Worktile社区 k8s中如何使用go 在K8s编程中如何使用Go-阿里云开发者社区 go build - o : -o&#xff1a;指定输出文件…...

Python及C++中的字典

一、Python中的字典 &#xff08;一&#xff09;基本概念 字典&#xff08;dict&#xff09;是Python中一种可变容器模型&#xff0c;用于存储键值对&#xff08;key:value&#xff09;。字典的键必须是不可变类型&#xff08;如字符串、数字或元组&#xff09;&#xff0c;而…...

.net Core 和 .net freamwork 调用 deepseek api 使用流输出文本(对话补全)

.net Core 调用 deepseek api 使用流输出文本 简下面直接上代码&#xff08;.net core&#xff09;&#xff1a;最后再贴一个 .net Freamwork 4 可以用的代码TLS 的代码至关重要的&#xff1a;&#xff08;下面这个&#xff09; 简 在官网里面有许多的案例&#xff1a;我们通过…...

[特殊字符] 第十三讲 | 地统计模拟与空间不确定性评估

&#x1f4d8; 专栏&#xff1a;科研统计方法实战分享 | 地学/农学人的数据分析工具箱 ✍️ 作者&#xff1a;平常心0715 &#x1f3af; 关键词&#xff1a;地统计模拟、随机函数、空间不确定性、条件模拟、SGS、R语言 &#x1f9e0; 核心导语 在现实数据有限、空间异质性强的…...

springcloud整理

问题1.服务拆分后如何进行服务之间的调用 我们该如何跨服务调用&#xff0c;准确的说&#xff0c;如何在cart-service中获取item-service服务中的提供的商品数据呢&#xff1f; 解决办法&#xff1a;Spring给我们提供了一个RestTemplate的API&#xff0c;可以方便的实现Http请…...

04-算法打卡-数组-二分查找-leetcode(69)-第四天

1 题目地址 69. x 的平方根 - 力扣&#xff08;LeetCode&#xff09;69. x 的平方根 - 给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被 舍去 。注意&#xff1a;不允许使用任何内…...

[Windows] 字体渲染 mactype v2025.4.11

[Windows] 字体渲染 mactype 链接&#xff1a;https://pan.xunlei.com/s/VONeCUP2hEgO5WIQImgtGUmrA1?pwdyruf# 025.4.11 Variable font support 可变字体支持已到来。 本版本将可变字体支持扩展到所有 GDI 应用程序。 所有 win32 程序中的字体&#xff0c;如 Noto Sans、Se…...

VSCode CMake调试CPP程序

文章目录 1 安装C与CMake插件2 配置CMakeLists.txt3 使用CMake编译调试3.1 编译3.2 调试 4 自定义构建调试参考 1 安装C与CMake插件 C插件 CMake插件 2 配置CMakeLists.txt 编写测试程序 #include<iostream>int main(int argc, char const *argv[]) {int a 1, b 2;i…...

MySQL数据过滤、转换与标准化

数据处理是数据库操作的重要组成部分&#xff0c;尤其是在大量数据中查找、转换和规范化目标信息的过程中。为了确保数据的有效性与一致性&#xff0c;MySQL提供了一系列数据过滤、转换与标准化的功能。 本教程将深入探讨数据过滤和转换的基本方法及应用&#xff0c;内容涵盖数…...

Halo 设置 GitHub - OAuth2 认证指南

在当今数字化时代&#xff0c;用户认证的便捷性和安全性愈发重要。对于使用 Halo 搭建个人博客或网站的开发者而言&#xff0c;引入 GitHub - OAuth2 认证能够极大地提升用户登录体验。今天&#xff0c;我们就来详细探讨一下如何在 Halo 中设置 GitHub - OAuth2 认证。 一、为…...

【unity游戏开发——Animator动画】Animator动画状态机复用——重写动画控制器 Animator Override Controller

注意&#xff1a;考虑到UGUI的内容比较多&#xff0c;我将UGUI的内容分开&#xff0c;并全部整合放在【unity游戏开发——Animator动画】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 文章目录 一、状态机复用是什么&#xff1f;二、实战专栏推荐完结 一、状态机复…...

C语言--汉诺塔问题

汉诺塔问题是一个典型的递归问题。 递归问题的基本思想&#xff1a;将问题逐步化简为相同思路但是规模更小的问题&#xff0c;直到问题可以直接解决 递归的关键在于基准情形和递归步骤&#xff0c;基准情形也就是退出条件&#xff0c;递归步骤也就是把问题简化为子问题的过程。…...

301.找出3位偶数

2094. 找出 3 位偶数 - 力扣&#xff08;LeetCode&#xff09; class Solution {List<Integer> resnew ArrayList<>();List<Integer> linew ArrayList<>();public int[] findEvenNumbers(int[] digits) {Arrays.sort(digits);boolean[] numsnew boolea…...

使用Python从零开始构建端到端文本到图像 Transformer大模型

简介&#xff1a;通过特征向量从文本生成图像 回顾&#xff1a;多模态 Transformer 在使用Python从零实现一个端到端多模态 Transformer大模型中&#xff0c;我们调整了字符级 Transformer 以处理图像&#xff08;通过 ResNet 特征&#xff09;和文本提示&#xff0c;用于视觉…...

comfyui点击执行没反应一例

以前只遇到过执行工作流时出错的情况&#xff0c;从没试过说点了执行后一点反应都没有的情况。 今天下载 蓝色多脑盒《一键同时换头、换脸、发型、发色之双pulid技巧》分享的工作&#xff0c;第一次发现点击执行没反应。 发现左下角的地方连接错乱了。 更正连接后工作流能正常…...

01-libVLC的视频播放器:环境搭建以及介绍

项目展示项目播放器 VLC简介VLC媒体播放器(VideoLAN Client)是一款开源、跨平台的自由多媒体播放器,由VideoLAN项目开发。它支持众多音频与视频格式(如MPEG-2、MPEG-4、H.264、MKV、WebM、WMV、MP3等),以及DVD、VCD和各种流媒体协议。 VLC的特点跨平台支持:Windows、mac…...

用css画一条弧线

ui里有一条弧线&#xff0c;现在用css实现 关键代码 border-bottom-left-radius: 100% 7px 两个参数分别代表横向和纵向的深度border-bottom-right-radius: 100% 7px...

FPGA上实现SD卡连续多块读的命令

在FPGA上实现SD卡连续多块读的命令 CMD17命令一次只能读取1个块 CMD18命令一次可以连续读取多个块&#xff0c;直到停止命令CMD12 CMD18命令读的块数程序可任意设置 目录 前言 一、SD卡多块读命令CMD18 二、停止读命令CMD12 三、SD卡初始化SD卡连续块读操作的verilog代码 …...

从原理图到成品:PCBA设计的常见陷阱与规避方法

在电子设计领域&#xff0c;从原理图到PCBA&#xff08;Printed Circuit Board Assembly&#xff09;的转换过程中&#xff0c;布局布线是决定电路性能的关键环节。然而&#xff0c;许多工程师在实际操作中容易陷入一些常见误区&#xff0c;导致信号完整性、EMI&#xff08;电磁…...

强化学习信用分配——以RLHF为例分析

Section 1. 信用分配简介 在强化学习中&#xff0c;信用分配&#xff08;Credit Assignment&#xff09;是指确定某个动作或状态对最终奖励的具体贡献的过程。由于强化学习的奖励通常是延迟的&#xff08;Delayed Reward&#xff09;&#xff0c;比如围棋&#xff0c;只有在胜…...