clean code-代码整洁之道 阅读笔记(第十七章 终章)

大纲

第十七章 味道与启发

17.1 注释

C1:不恰当的信息

C2:废弃的注释

C3:冗余注释

C4:糟糕的注释

C5:注释掉的代码

17.2 环境

E1:需要多步才能实现的构建

E2:需要多步才能做到的测试

17.3 函数

F1:过多的参数

F2:输出参数

F3:标识参数

F4:死函数

17.4 一般性问题

G1:一个源文件中存在多种语言

G2:明显的行为未被实现

G3:不正确的边界行为

G4:忽视安全

G5:重复

G6:在错误的抽象层级上的代码

G7:基类依赖于派生类

G8:信息过多

G9:死代码

G10:垂直分隔

G11:前后不一致

G12:混淆视听

G13:人为耦合

G14:特性依恋

G15:选择算子参数

G16:晦涩的意图

G17:位置错误的权责

G18:不恰当的静态方法

G19:使用解释性变量

G20:函数名称应该表达其行为

G21:理解算法

G22:把逻辑依赖改为物理依赖

G23:用多态替代lf/Else或Switch/Case

G24:遵循标准约定

G25:用命名常量替代魔术数

G26:准确

G27:结构甚于约定

G28:封装条件

G29:避免否定性条件

G30:函数只该做一件事

G31:掩蔽时序耦合

G32:别随意

G33:封装边界条件

G34:函数应该只在一个抽象层级上

G35:在较高层级放置可配置数据

G36:避免传递浏览

17.5 Java

J1:通过使用通配符避免过长的导入清单

J2:不要继承常量

J3:常量vs.枚举

17.6 名称

N1:采用描述性名称

N2:名称应与抽象层级相符

N3:尽可能使用标准命名法

N4:无歧义的名称

N5:为较大作用范围选用较长名称

N6:避免编码

N7:名称应该说明副作用 

17.7 测试

T1:测试不足

T2:使用覆盖率工具

T3:别略过小测试

T4:被忽略的测试就是对不确定事物的疑问

T5:测试边界条件

T6:全面测试相近的缺陷

T7:测试失败的模式有启发性

T8:测试覆盖率的模式有启发性

T9:测试应该快速

17.8小结


第十七章 味道与启发

17.1 注释

C1:不恰当的信息

        注释只应该描述有关代码和设计的技术性信息。

C2:废弃的注释
C3:冗余注释
C4:糟糕的注释

        使用正确的语法和拼写。别闲扯,别画蛇添足,,保持简洁。

C5:注释掉的代码

        看到注释掉的代码,就删除它!别担心,源代码控制系统还会记得它。

17.2 环境

E1:需要多步才能实现的构建

        构建系统应该是单步的小操作。不应该从源代码控制系统中一小点一小点签出代码。不应该需要一系列神秘指令或环境依赖脚本来构建单个元素。不应该四处寻找额外的 JAR、XML文件和其他系统所需的杂物。你应当能够用单个命令签出系统,并用单个指令构建它。

E2:需要多步才能做到的测试

        你应当能够发出单个指令就可以运行全部单元测试。能够运行全部测试是如此基础和重要,应该快速、轻易和直截了当地做到。

17.3 函数

F1:过多的参数

        函数的参数量应该少。没参数最好,一个次之,两个、三个再次之。三个以上的参数非常值得质疑,应坚决避免。

F2:输出参数

        输出参数违反直觉。如果函数非要修改什么东西的状态不可,就修改它所在对象的状态。

F3:标识参数

        布尔值参数大声宣告函数做了不止一件事。它们令人迷惑,应该消灭掉。

F4:死函数

        永不被调用的方法应该丢弃。

17.4 一般性问题

G1:一个源文件中存在多种语言

        理想的源文件包括且只包括一种语言。现实上,我们可能会不得不使用多于一种语言但应该尽力减少源文件中额外语言的数量和范围。

G2:明显的行为未被实现

        遵循"最小惊异原则"(The Principle of Least Surprise),函数或类现其他程序员有理由期待的行为。例如,考虑一个将日期名称翻译为表示该日期的枚举的函数。

Day day = DayDate.StringToDay(String dayName);

        我们期望字符串 Monday 翻译为 Day.MONDAY。我们也期望常用缩写形式也能被翻译出来,我们还期待函数忽略大小写。
        如果明显的行为未被实现,读者和用户就不能再依靠他们对函数名称的直觉。他们不再信任原作者,不得不阅读代码细节。

G3:不正确的边界行为

        没什么可以替代谨小慎微。每种边界条件、每种极端情形、每个个异常都代表了某种可能搞乱优雅而直白的算法的东西。别依赖直觉。追索每种边界条件,并编写测试。

G4:忽视安全

        忽视安全相当危险。手工控制serialVersionUID可能有必要,但总会有风险。关闭某些编译器警告(或者全部警告!)可能有助于构建成功,但也存在陷于无穷无尽的调试的风险。关闭失败测试、告诉自己过后再处理,这和假装刷信用卡不用还钱一样坏。

G5:重复

        本书提到的最重要的规则之一。

        每次看到重复代码,都代表遗漏了抽象。

        重复最明显的形态是你不断看到明显一样的代码,可以用单一方法来替代之。

        较隐蔽的形态是在不同模块中不断重复出现、检测同一组条件的switch/case或if/else链。可以用多态来替代之。

        更隐蔽的形态是采用类似算法但具体代码行不同的模块。这也是一种重复,可以使用模板方法模式或策略模式来修正。

G6:在错误的抽象层级上的代码

        创建分离较高层级一般性概念与较低层级细节概念的抽象模型。

        所有较低层级概念放在派生类中,所有较高层级概念放在基类中。

        要点是不能就错误放置的抽象模型撒谎。孤立抽象是软件开发者最难做到的事之一,而且一旦做错也没有快捷的修复手段。

G7:基类依赖于派生类

        通常来说,基类对派生类应该一无所知。

        有时,派生类数量严格固定,而基类中拥有在派生类之间选择的代码 。解决方法:把派生类和基类部署到不同的jar文件中。

G8:信息过多

        优秀的软件开发人员学会限制类或模块中暴露的接口数量。类中的方法越少越好。函数知道的变量越少越好。类拥有的实体变量越少越好。
        隐藏你的数据。隐藏你的工具函数。隐藏你的常量和你的临时变量。不要创建拥有大量方法或大量实体变量的类。不要为子类创建大量受保护变量和函数。尽力保持接口紧凑。通过限制信息来控制耦合度。

G9:死代码

        死代码就是不执行的代码。可以在检查不会发生的条件的if语句体中找到。可以在从不抛出异常的try语句的catch块中找到。可以在从不被调用的小工具方法中找到,也可以在永不会发生的switch/case条件中找到。
        死代码的问题是过不久它就会发出臭味。时间越久,味道就越酸臭。这是因为,在设计改变时,死代码不会随之更新。它还能通过编译,但并不会道循较新的约定或规则。它编写的时候,系统是另一番模样。如果你找到死代码,就体面地地埋葬它,将它从系统中删除掉。

G10:垂直分隔

        变量和函数应该在靠近被使用的地方定义。本地变量应该正好在其首次被使用的位置上面声明,垂直距离要短。本地变量不该在其被使用之处几百行以外声明。

        私有函数应该刚好在其首次被使用的位置下面定义。

G11:前后不一致

        小心选择约定,一旦选中,就小心持续遵循。

G12:混淆视听

        没有实现的默认构造器、没有用到的变量、从不调用的函数、没有信息量的注释等等,这些都是应该移除的废物。保持源文件整洁,良好地组织,不被搞乱。

G13:人为耦合

        一般来说,人为耦合是指两个没有直接目的之间的模块的耦合。其根源是将变量、常量或函数不恰当地放在临时方便的位置。这是种漫不经心的偷懒行为。花点时间研究应该在什么地方声明函数、常量和变量。不要为了力便随手放置,然后置之不理。

G14:特性依恋

        类的方法只应对其所属类中的变量和函数感兴趣,不该垂青其他类中的变量和函数。当方法通过某个其他对象的访问器和修改器来操作该对象内部数据,则它就依恋于该对象所属类的范围。它期望自己己在那个类里面,这样就能直接访问它操作的变量。

G15:选择算子参数

        算子可能是布尔类型、枚举元素、整数或任何一种用于选择函数行为的参数。使用多个函数,通常优于向单个函数传递某些代码来选择函数行为。

G16:晦涩的意图

        代码要尽可能具有表达力。联排表达式、匈牙利语标记法和魔术数都遮蔽了作者的意图。
例如,下面是overTimePay函数可能的一种表现形式:

public int m_otCalc(){
    return iThsWkd * iThsRte+ (int)Math.round(0.5 * iThsRte * Math.max(0, iThsWkd-400));
}


        它既短小又紧凑,但实际上不可捉摸。值得花时间将代码的意图呈现给读者。

G17:位置错误的权责

        代码应该放在读者自然而然期待它所在的地方。

G18:不恰当的静态方法

        通常应该倾向于选用非静态方法。如果有疑问,就是用非静态函数。如果的确需要静态函数,确保没机会打算让它有多态行为。

G19:使用解释性变量

        让程序可读的最有力方法之一,就是将计算过程打散成在用有意义的单词命名变量中放置中间值

        这事很难做过火。解释性变量多比少好。只要把计算过程打散成一系列良好命名的中间值,不透明的模块就会突然变得透明,这很值得注意。

G20:函数名称应该表达其行为

        如果你必须查看函数的实现(或文档)才知道它是做什么的,就该换个更好的函数名,或者重新安排功能代码,放到有较好名称的函数中。

G21:理解算法

        在你认为自己完成某个函数之前,确认自己理解了它是怎么工作的。通过全部测试还不够好。你必须知道解决方案是正确的。
        获得这种知识和理解的最好途径,往往是重构函数,得到某种整洁而足具表达力、清楚呈示如何工作的东西。

G22:把逻辑依赖改为物理依赖

        如果某个模块依赖于另一个模块,依赖就该是物理上的而不是逻辑再上的。依赖者模块不应对被依赖者模块有假定(换言之,逻辑依赖)。它应当明确地询问后者全部信息。

G23:用多态替代lf/Else或Switch/Case

        "单个switch"规则:对于给定的选择类型,不应有多于一个switch语句。在那个switch语句中的多个case,必须创建多态对象,取代系统中其他类似switch语句。

G24:遵循标准约定

        每个团队都应遵循基于通用行业规范的一套编码标准。编码标准维应指定诸如在何处声明实体变量,如何命名类,方法和变量,在何处放置括号等等。团队不应用文档描述这些约定,因为代码本身提供了范例。

G25:用命名常量替代魔术数
G26:准确

        在代码中做决定时,确认自己足够准确。明确自己为何要这么做,如果遇到异常情况如何处理。别懒得理会决定的准确性。如果你打算调用可能返回null的函数,确认自己检查了null值。如果查询你认为是数据库中唯一的记录,确保代码检查不存在其他记录。如果要处理货币数据,使用整数,并恰当地处理四舍五入。如果可能有并发更新,确认你实现了某种锁定机制。
        代码中的含糊和不准确要么是意见不同的结果,要么源于懒惰。无论原因是什么,都要消除。

G27:结构甚于约定

        坚守结构甚于约定的设计决策。命名约定很好,但却次于强制性的结构。

G28:封装条件

        如果没有if或while语句的上下文,布尔逻辑就难以理解。应该把解释了条件意图的函数抽离出来。

//例如:
if(shouldBeDeleted(timer))
//要好于
if(timer.hasExpired() && !timer.isRecurrent())
G29:避免否定性条件

        否定式要比肯定式难明白一些。所以,尽可能将条件表示为肯定形式。

G30:函数只该做一件事
G31:掩蔽时序耦合

        常常有必要使用时序耦合,但你不应该掩蔽它。排列函数参数,好让它们被调用的次序显而易见。

 =》

G32:别随意

        构建代码需要理由,而且理由应与代码结构相契合。如果结构显得太随意,其他人就会想修改它。如果结构自始至终保持一致,其他人就会使用它,并且遵循其约定。

G33:封装边界条件

        边界条件难以追踪。把处理边界条件的代码集中到一处,不要散落于代码中。

G34:函数应该只在一个抽象层级上

        函数中的语句应该在同一抽象层级上,该层级应该是函数名所示操作的下一层。

G35:在较高层级放置可配置数据

        如果你有个已知并该在较高抽象层级的默认常量或配置值,不要将它埋藏到较低层级的函数中。把它作为较高层级函数调用较低层级函数时的一个参数。

G36:避免传递浏览

        正确的做法是让直接协作者提供所需的全部服务。不必逛遍系统的对象全图,搜寻我们
要调用的方法。

17.5 Java

J1:通过使用通配符避免过长的导入清单
J2:不要继承常量
J3:常量vs.枚举

        别再用public static final int,而是使用枚举。

17.6 名称

N1:采用描述性名称
N2:名称应与抽象层级相符

        不要取沟通实现的名称;取反映类或函数抽象层级的名称。

N3:尽可能使用标准命名法
N4:无歧义的名称
N5:为较大作用范围选用较长名称
N6:避免编码

        不应在名称中包括类型或作用范围信息。

N7:名称应该说明副作用 

        名称应该说明函数、变量或类的一切信息。不要用名称掩蔽副作用。不要用简单的动词来描述做了不止一个简单动作的函数。

17.7 测试

T1:测试不足
T2:使用覆盖率工具
T3:别略过小测试
T4:被忽略的测试就是对不确定事物的疑问
T5:测试边界条件
T6:全面测试相近的缺陷
T7:测试失败的模式有启发性
T8:测试覆盖率的模式有启发性
T9:测试应该快速

17.8小结

        这份启发与味道的清单很难说已完备无缺。我不能确定这样一份清单会不会完备无缺。但或许完整性不该是目标,因为该清单确实给出了一套价值体系那套价值体系才该是目标,也是本书的主题所在。整洁代码并非遵循一套规则写就。学习一系列启发并不足以让你成为软件匠人。专业性和技艺来自于驱动规程的价值观。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

computed传参

目录 一、问题 二、解决方法 三、总结 tiips:如嫌繁琐,直接移步总结即可! 一、问题 computed可以依据其他变量动态计算出值,但是v-for渲染html时,需要根据html中 传入的不同变量,来分别做处理或者利用 html中的值该…

node+vue的仿网易云音乐网站-计算机毕业设计源码02905

摘要 本论文基于Node.js和Vue.js技术,以仿网易云音乐网站为目标,设计并实现了一个具有音乐播放、歌曲搜索、歌单推荐等功能的音乐网站。首先,论文介绍了网易云音乐网站的特点和功能,分析了其前端和后端技术栈,为后续设…

CTFShow的36D杯

神光 还是想了一下,但那个异或搞不出来,都是对dword_41A038操作,想起开头就给了 key ,还有反调试应该是要调试的 输出的应该就是 flag ,只是为什么是乱码呢? 放 od 再试试,直接就出 flag 了&am…

Docker搭建kafka+zookeeper以及Springboot集成kafka快速入门

参考文章 【Docker安装部署KafkaZookeeper详细教程】_linux arm docker安装kafka-CSDN博客 Docker搭建kafkazookeeper 打开我们的docker的镜像源配置 vim /etc/docker/daemon.json 配置 { "registry-mirrors": ["https://widlhm9p.mirror.aliyuncs.com"…

iPhone数据恢复篇:iPhone 数据恢复软件有哪些

问题:iPhone 15 最好的免费恢复软件是什么?我一直在寻找一个恢复程序来恢复从iPhone中意外删除的照片,联系人和消息,但是我有很多选择。 谷歌一下,你会发现许多付费或免费的iPhone数据恢复工具,声称它们可…

[C++]——同步异步日志系统(3)

同步异步日志系统 一、日志系统框架设计1.1模块划分1.1.1 日志等级模块1.1.2 日志消息模块1.1.3 日志消息格式化模块1.1.4 日志落地模块(日志落地的方向是工厂模式)1.1.5 日志器模块(日志器的生成是建造者模式)1.1.6 异步线程模块…

【HBZ分享】TCP连接完成后又是如何保证数据的可靠性传输

前提 发送发发送数据时,需要给出一个seq编号。第一个数据包的seq编号是一个随机数, 从第二个开始,seq编号就是【第一次的seq数据包大小】, 即接收方响应过来的期待数据包编号 ACK机制 接收方收到数据后,要给发送方回…

Html5前端基本知识整理与回顾下篇

今天我们继续结合发布的Html5基础知识点文档进行复习,希望对大家有所帮助。 目录 列表 无需列表 有序列表 自定义列表 样例 表格 基本属性 ​编辑 相关属性 Border Width Height ​编辑 表格标题 ​编辑 表格单元头 合并单元格 垂直单元格合并 水…

实践致知第12享:如何新建一个Word并设置格式

一、背景需求 小姑电话说:要新建一个Word文档,并将每段的首行设置空2格。 二、解决方案 1、在电脑桌面上空白地方,点击鼠标右键,在下拉的功能框中选择“DOC文档”或“DOCX文档”都可以,如下图所示。 之后&#xff0…

npm install失败,数据源过期

npm install时报错&#xff1a;“Unexpected token &#xff1c; in JSON at position 0 while parsing near ‘&#xff1c;!DOCTYPE html&#xff1e;” 执行Vue2项目安装时&#xff0c;出现报错了&#xff0c;显示ERROr: **npm ERR! Unexpected token < in JSON at posi…

58、基于径向基神经网络的曲线拟合(matlab)

1、基于径向基神经网络的曲线拟合简介及原理 1&#xff09;原理简介 基于径向基神经网络&#xff08;Radial Basis Function Neural Network, RBFNN&#xff09;的曲线拟合是一种常用的非线性拟合方法&#xff0c;通过在输入空间中使用径向基函数对数据进行处理&#xff0c;实…

cesium 雷达扫描

cesium 雷达扫描 (下面附有源码) 实现思路 1、通过改变圆型材质来实现效果, 2、用了模运算和步进函数(step)来创建一个重复的圆形图案 3、当纹理坐标st落在垂直或水平的中心线上时,该代码将改变透明度和颜色,以突出显示这些线 示例代码 <!DOCTYPE html> <ht…

完美解决windows开机时,系统提示此windows副本不是正版的正确解决方法,亲测有效!!!

完美解决windows开机时&#xff0c;系统提示此windows副本不是正版的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 亲测有效 完美解决windows开机时&#xff0c;系统提示此windows副本不是正版的正确解决方法&#xff0c;亲测有效&#xff01;&#…

二分查找3

1. 有序数组中的单一元素&#xff08;540&#xff09; 题目描述&#xff1a; 算法原理&#xff1a; 二分查找解题关键就在于去找到数组的二段性&#xff0c;这里数组的二段性是从单个数字a开始出现然后分隔出来的&#xff0c;如果mid落入左半部分那么当mid为偶数时nums[mid1]…

来聊聊Redis持久化AOF管道通信的设计

写在文章开头 最近遇到很多烦心事&#xff0c;希望通过技术来得以放松&#xff0c;今天这篇文章笔者希望会通过源码的方式分析一下AOF如何通过Linux父子进程管道通信的方式保证进行AOF异步重写时还能实时接收用户处理的指令生成的AOF字符串&#xff0c;从而保证尽可能的可靠性…

window 安装 openssl

文章目录 前言window 安装 openssl1. 下载2. 安装3. 配置环境变量4. 测试 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话…

LVS集群及其它的NAT模式

1.lvs集群作用&#xff1a;是linux的内核层面实现负载均衡的软件&#xff1b;将多个后端服务器组成一个高可用、高性能的服务器的集群&#xff0c;通过负载均衡的算法将客户端的请求分发到后端的服务器上&#xff0c;通过这种方式实现高可用和负载均衡。 2.集群和分布式&#…

Mattermost:一个强大的开源协作平台

Mattermost是一个强大的开源协作平台&#xff0c;基于云原生架构&#xff0c;为企业级用户提供安全、可扩展且自托管的消息传递解决方案。 一、平台特点 开源与定制性&#xff1a;Mattermost是一个开源项目&#xff0c;用户可以根据自身需求定制界面、添加功能或扩展其功能&am…

百川工作手机实现销售管理微信监控系统

在瞬息万变的商业战场中&#xff0c;每一分效率的提升都是企业制胜的关键。传统销售管理模式已难以满足现代企业对精准、高效、合规的迫切需求。今天&#xff0c;让我们一同探索如何利用工作手机这一创新工具&#xff0c;为您的销售团队装上智能翅膀&#xff0c;开启销售管理的…