【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// ...}}
除了计算法定最少数量假期的部分,accrueUSDivisionVacation和accrue-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条简单的规则,跟着做就能帮助你创建优良的设计,会如何?假使遵循这些规则,你就能洞见代码的结构和设计,更能轻易地应用SRP和DIP之类的原则,便会如何&…...
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年的今天,人工智能已从技术概念全面渗透至产业核心。中国作为全球AI技术应用的前沿阵地,正通过“人工智能”行动加速推进技术与实体经济深度融合。 这一背景下,永洪科技凭借其“国内某头部ICT人力资源板块GenAI项目”荣获“2025全国企业…...
基于疾风大模型的新能源储能优化系统:方法、实现与案例分析
一、引言 随着可再生能源渗透率不断提高,储能系统在电力系统中的重要性日益凸显。传统储能控制方法主要基于规则策略和简单优化算法,难以应对高比例新能源场景下的复杂决策需求。本文将详细介绍如何利用疾风大模型(Gale Model)构建智能化的新能源储能优化系统,包含核心方…...
菊风RTC 2.0 开发者文档正式发布,解锁音视频新体验!
重磅发布! 开发者们,菊风实时音视频2.0文档已正式发布上线,为您提供更清晰、更高效的开发支持!让菊风实时音视频2.0为您的音视频应用加速~ 菊风实时音视频2.0聚焦性能升级、体验升级、录制服务升级,助力视频通话、语…...
12c补丁滚动升级
12c打补丁前置检查 备份文件,可以不做,因为文件可能很大,如果可以备份整个安装文件。 1.check grid: % /u01/app/12.1.0/grid/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -phBaseDir /home/software/27010872/2691…...
OpenCv高阶(一)——图像金字塔(上采样、下采样)
目录 图像金字塔 一、上下采样原理 1、向下取样 2、向上采样 3、图像金字塔的作用 二、案例实现 1、高斯下采样 2、高斯金字塔中的上采样 3、对下采样的结果做上采样,图像变模糊,无法复原 4、拉普拉斯金字塔(图片复原) 图…...
LEARNING DYNAMICS OF LLM FINETUNING【论文阅读笔记】
LEARNING DYNAMICS OF LLM FINETUNING 一句话总结 作者将LLM的学习动力机制拆解成AKG三项,并分别观察了SFT和DPO训练过程中正梯度信号和负梯度信号的变化及其带来的影响,并得到以下结论: SFT通过梯度相似性间接提升无关…...
数据集 | 沥青路面缺陷目标检测
文章目录 一、数据集概述1. 行业痛点与数据集价值2. 数据集技术规格 二、样本类别详解1. 裂缝 (Crack)2. 裂缝修补 (Crack Repair)3. 坑洞 (Pothole)4. 坑洞修补 (Pothole Repair)5. 井盖 (Manhole Cover)6. 其他 (Other) 三、标注工具四、下载地址 一、数据集概述 1. 行业痛点…...
AllData数据中台升级发布 | 支持K8S数据平台2.0版本
🔥🔥 AllData大数据产品是可定义数据中台,以数据平台为底座,以数据中台为桥梁,以机器学习平台为中层框架,以大模型应用为上游产品,提供全链路数字化解决方案。 ✨杭州奥零数据科技官网…...
第二十二: go与k8s、docker相关编写dockerfile
实战演示k8s部署go服务,实现滚动更新、重新创建、蓝绿部署、金丝雀发布-CSDN博客 go 编写k8s命令: 怎么在go语言中编写k8s命令 • Worktile社区 k8s中如何使用go 在K8s编程中如何使用Go-阿里云开发者社区 go build - o : -o:指定输出文件…...
Python及C++中的字典
一、Python中的字典 (一)基本概念 字典(dict)是Python中一种可变容器模型,用于存储键值对(key:value)。字典的键必须是不可变类型(如字符串、数字或元组),而…...
.net Core 和 .net freamwork 调用 deepseek api 使用流输出文本(对话补全)
.net Core 调用 deepseek api 使用流输出文本 简下面直接上代码(.net core):最后再贴一个 .net Freamwork 4 可以用的代码TLS 的代码至关重要的:(下面这个) 简 在官网里面有许多的案例:我们通过…...
[特殊字符] 第十三讲 | 地统计模拟与空间不确定性评估
📘 专栏:科研统计方法实战分享 | 地学/农学人的数据分析工具箱 ✍️ 作者:平常心0715 🎯 关键词:地统计模拟、随机函数、空间不确定性、条件模拟、SGS、R语言 🧠 核心导语 在现实数据有限、空间异质性强的…...
springcloud整理
问题1.服务拆分后如何进行服务之间的调用 我们该如何跨服务调用,准确的说,如何在cart-service中获取item-service服务中的提供的商品数据呢? 解决办法:Spring给我们提供了一个RestTemplate的API,可以方便的实现Http请…...
04-算法打卡-数组-二分查找-leetcode(69)-第四天
1 题目地址 69. x 的平方根 - 力扣(LeetCode)69. x 的平方根 - 给你一个非负整数 x ,计算并返回 x 的 算术平方根 。由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。注意:不允许使用任何内…...
[Windows] 字体渲染 mactype v2025.4.11
[Windows] 字体渲染 mactype 链接:https://pan.xunlei.com/s/VONeCUP2hEgO5WIQImgtGUmrA1?pwdyruf# 025.4.11 Variable font support 可变字体支持已到来。 本版本将可变字体支持扩展到所有 GDI 应用程序。 所有 win32 程序中的字体,如 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数据过滤、转换与标准化
数据处理是数据库操作的重要组成部分,尤其是在大量数据中查找、转换和规范化目标信息的过程中。为了确保数据的有效性与一致性,MySQL提供了一系列数据过滤、转换与标准化的功能。 本教程将深入探讨数据过滤和转换的基本方法及应用,内容涵盖数…...
Halo 设置 GitHub - OAuth2 认证指南
在当今数字化时代,用户认证的便捷性和安全性愈发重要。对于使用 Halo 搭建个人博客或网站的开发者而言,引入 GitHub - OAuth2 认证能够极大地提升用户登录体验。今天,我们就来详细探讨一下如何在 Halo 中设置 GitHub - OAuth2 认证。 一、为…...
【unity游戏开发——Animator动画】Animator动画状态机复用——重写动画控制器 Animator Override Controller
注意:考虑到UGUI的内容比较多,我将UGUI的内容分开,并全部整合放在【unity游戏开发——Animator动画】专栏里,感兴趣的小伙伴可以前往逐一查看学习。 文章目录 一、状态机复用是什么?二、实战专栏推荐完结 一、状态机复…...
C语言--汉诺塔问题
汉诺塔问题是一个典型的递归问题。 递归问题的基本思想:将问题逐步化简为相同思路但是规模更小的问题,直到问题可以直接解决 递归的关键在于基准情形和递归步骤,基准情形也就是退出条件,递归步骤也就是把问题简化为子问题的过程。…...
301.找出3位偶数
2094. 找出 3 位偶数 - 力扣(LeetCode) class Solution {List<Integer> resnew ArrayList<>();List<Integer> linew ArrayList<>();public int[] findEvenNumbers(int[] digits) {Arrays.sort(digits);boolean[] numsnew boolea…...
使用Python从零开始构建端到端文本到图像 Transformer大模型
简介:通过特征向量从文本生成图像 回顾:多模态 Transformer 在使用Python从零实现一个端到端多模态 Transformer大模型中,我们调整了字符级 Transformer 以处理图像(通过 ResNet 特征)和文本提示,用于视觉…...
comfyui点击执行没反应一例
以前只遇到过执行工作流时出错的情况,从没试过说点了执行后一点反应都没有的情况。 今天下载 蓝色多脑盒《一键同时换头、换脸、发型、发色之双pulid技巧》分享的工作,第一次发现点击执行没反应。 发现左下角的地方连接错乱了。 更正连接后工作流能正常…...
01-libVLC的视频播放器:环境搭建以及介绍
项目展示项目播放器 VLC简介VLC媒体播放器(VideoLAN Client)是一款开源、跨平台的自由多媒体播放器,由VideoLAN项目开发。它支持众多音频与视频格式(如MPEG-2、MPEG-4、H.264、MKV、WebM、WMV、MP3等),以及DVD、VCD和各种流媒体协议。 VLC的特点跨平台支持:Windows、mac…...
用css画一条弧线
ui里有一条弧线,现在用css实现 关键代码 border-bottom-left-radius: 100% 7px 两个参数分别代表横向和纵向的深度border-bottom-right-radius: 100% 7px...
FPGA上实现SD卡连续多块读的命令
在FPGA上实现SD卡连续多块读的命令 CMD17命令一次只能读取1个块 CMD18命令一次可以连续读取多个块,直到停止命令CMD12 CMD18命令读的块数程序可任意设置 目录 前言 一、SD卡多块读命令CMD18 二、停止读命令CMD12 三、SD卡初始化SD卡连续块读操作的verilog代码 …...
从原理图到成品:PCBA设计的常见陷阱与规避方法
在电子设计领域,从原理图到PCBA(Printed Circuit Board Assembly)的转换过程中,布局布线是决定电路性能的关键环节。然而,许多工程师在实际操作中容易陷入一些常见误区,导致信号完整性、EMI(电磁…...
强化学习信用分配——以RLHF为例分析
Section 1. 信用分配简介 在强化学习中,信用分配(Credit Assignment)是指确定某个动作或状态对最终奖励的具体贡献的过程。由于强化学习的奖励通常是延迟的(Delayed Reward),比如围棋,只有在胜…...
