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

2024-06-20 00:44

本文主要是介绍clean code-代码整洁之道 阅读笔记(第十章),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第十章 类

10.1 类的组织

        遵循标准的Java约定,类应该从一组变量列表开始。如果有公共静态常量,应该先出现。然后是私有静态变量,以及私有实体变量。很少会有公共变量。公共函数应跟在变量列表之后。我们喜欢把由某个公共函数调用的私有工具函数紧随在该公共函数后面。这符合了自顶向下原则,让程序读起来就像一篇报纸文章。

        若同一程序包内的某个测试需要调用一个函数或变量,我们就会将该函数或变量置为受保护或在整个程序包内可访问。

10.2 类应该短小

        对于函数,我们通过计算代码行数衡量大小。对于类,我们采用不同的衡量方法,计算权责(responsibility)。

        类的名称应当描述其权责。实际上,命名正是帮助判断类的长度的第一个手段。我们也应该能够用大概25个单词简要描述一个类,且不用"若(if)"、"与(and)"、"或(or)"或者"但(but)"等词汇。

10.2.1 单一权责原则 

        单一权责原则(SRP):类或模块应有且只有一条加以修改的理由。

        鉴别权责(修改的理由)常常帮助我们在代码中认识到并创建出更好的抽象。

        让软件能工作和让软件保持整洁,是两种截然不同的工作。我们中的大多数人脑力有限,只能更多地把精力放在让代码能工作上,而不是放在保持代码有组织和整洁上。这全然正确分而治之,其在编程行为中的重要程度等同于在程序中的重要程度。

        再强调一下:系统应该由许多短小的类而不是少量巨大的类组成。每个小类封装一个权责,只有一个修改的原因,并与少数其他类一起协同达成期望的系统行为。

10.2.2 内聚

        类应该只有少量实体变量。类中的每个方法都应该操作一个或多个这种变量。通常而言,方法操作的变量越多,就越黏聚到类上。如果一个类中的每个变量都被每个方法所使用,则该类具有最大的内聚性。

        一般来说,创建这种极大化内聚类是既不可取也不可能的;另一方面,我们希望内聚性保持在较高位置。内聚性高,意味着类中的方法和变量互相依赖、互相结合成一个逻辑整体。

        保持函数和参数列表短小的策略,有时会导致为一组子集方法所用的实体变量数量增加。出现这种情况时,往往意味着至少有一个类要从大类中挣扎出来。你应当尝试将这些变量和方法分拆到两个或多个类中,让新的类更为内聚。

10.2.3 保持内聚性就会得到许多短小的类

        仅仅是将较大的函数切割为小函数,就将导致更多的类出现。

        当类丧失了内聚性,就拆分它!

        所以,将大函数拆为许多小函数,往往也是将类拆分为多个小类的时机。程序会更加有组织,也会拥有更为透明的结构。

10.3 为了修改而组织

        对于多数系统,修改将一直持续。每处修改都让我们冒着系统其他部分不能如期望般工作的风险。在整洁的系统中,我们对类加以组织,以降低修改的风险。

        代码清单10-9中的Sql类用来生成提供恰当元数据的SQL格式化字符事。这个类还没写完,所以暂时不支持update语句等SQL功能。当需要Sql类文支持update语句时,        我们就得"打开"这个类进行修改。打开类带来的问题是风险随之而来。对类的任何修改都有可能破坏类中的其他代码。必须全面重新测试。

        当增加一种新语句类型时,就要修改Sql类。改动单个语句类型时,也也要进行修改,比如打算让select功能支持子查询。存在两个修改的理由,说明Sql违反了SRP原则。可以从一条简单的组织性观点发现对SRP的违反。Sql的方法大纲显示,存在类似selectWithCriteria等只与select语句有关的私有方法。
        出现了只与类的一小部分有关的私有方法行为,意味着存在改进空间。然而,展开行动的基本动因却应该是系统的变动。若我们认为Sql类在逻辑上已具足,则无需担心对权责的拆分。如果在可预见的未来无需增加update功能,就该不去动Sql类。不过,一旦打开了类,就应当修正设计方案。

        我们希望将系统打造成在添加或修改特性时尽可能少惹麻烦的架子。在理想系统中,我们通过扩展系统而非修改现有代码来添加新特性。

隔离修改

需求会改变,所以代码也会改变。

        具体类包含实现细节(代码),而抽象类则只呈现概念。依赖于具体细节的客户类,当细节改变时,就会有风险。我们可以借助接口和抽象类来隔离这些细节带来的影响。

        部件之间的解耦代表着系统中的元素互相隔离得很好。隔离也让对系统每个元素的理解变得更加容易。

        通过降低连接度,我们的类就进循了另一条类设计原则,依赖倒置原则(Dependency Inversion Principle,DIP)。本质而言,DIP认为类应当依赖于抽象而不是依赖于具体细节。

示例:

        对具体细节的依赖给对系统的测试带来了挑战。如果我们构建一个依赖于外部TokyoStockExchangeAPI的Portfolio类,代表投资组合的价值,则测试用例就会受到价值查询的连带影响。如果每5分钟就有新说法,就很难写出测试来。

        与其设计直接依赖于TokyoStockExchange的Portfolio类,不如创建StockExchange接口,其中只声明一个方法:

public interface StockExchange {Money currentPrice(Stringsymbol);
}

        我们设计TokyoStockExchange类来实现这个接口。我们还要确保Portfoli的构造器接受作为参数的StockExchange引用:

public Portfolio{private StockExchange exchange;public Portfolio(StockExchange exchange) (this.exchange=exchange;}// ...
}
public class PortfolioTest {private FixedStockExchangeStub exchange;private Portfolio portfolio;@Beforeprotected void setUp() throws Exception{exchange= new FixedStockExchangeStub();exchange.fix("MSFT",100);portfolio = new Portfolio(exchange);}@Testpublic void Given FiveMSFTTotalshouldBe500() throws Exception {portfolio.add(5,"MSFT");Assert.assertEquals(500,portfolio.value());}
}

        我们的Portfolio类不再依赖于TokyoStockExchange类的实现细节,而是依赖于StockExchange接口。StockExchange接口呈现的是有关询问某只股票价格的抽象概念。这种抽象隔离了所有询价的特定细节,包括价格数据来自何处之类。

        尽管讨论了这么多关于代码语句及由代码语句构成的函数的表达力,除非我们将注意力放到代码组织的更高层面,就始终不能得到整洁的代码。

这篇关于clean code-代码整洁之道 阅读笔记(第十章)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1076621

相关文章

Java集合之Iterator迭代器实现代码解析

《Java集合之Iterator迭代器实现代码解析》迭代器Iterator是Java集合框架中的一个核心接口,位于java.util包下,它定义了一种标准的元素访问机制,为各种集合类型提供了一种统一的... 目录一、什么是Iterator二、Iterator的核心方法三、基本使用示例四、Iterator的工

Java 线程池+分布式实现代码

《Java线程池+分布式实现代码》在Java开发中,池通过预先创建并管理一定数量的资源,避免频繁创建和销毁资源带来的性能开销,从而提高系统效率,:本文主要介绍Java线程池+分布式实现代码,需要... 目录1. 线程池1.1 自定义线程池实现1.1.1 线程池核心1.1.2 代码示例1.2 总结流程2. J

JS纯前端实现浏览器语音播报、朗读功能的完整代码

《JS纯前端实现浏览器语音播报、朗读功能的完整代码》在现代互联网的发展中,语音技术正逐渐成为改变用户体验的重要一环,下面:本文主要介绍JS纯前端实现浏览器语音播报、朗读功能的相关资料,文中通过代码... 目录一、朗读单条文本:① 语音自选参数,按钮控制语音:② 效果图:二、朗读多条文本:① 语音有默认值:②

Vue实现路由守卫的示例代码

《Vue实现路由守卫的示例代码》Vue路由守卫是控制页面导航的钩子函数,主要用于鉴权、数据预加载等场景,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、概念二、类型三、实战一、概念路由守卫(Navigation Guards)本质上就是 在路

uni-app小程序项目中实现前端图片压缩实现方式(附详细代码)

《uni-app小程序项目中实现前端图片压缩实现方式(附详细代码)》在uni-app开发中,文件上传和图片处理是很常见的需求,但也经常会遇到各种问题,下面:本文主要介绍uni-app小程序项目中实... 目录方式一:使用<canvas>实现图片压缩(推荐,兼容性好)示例代码(小程序平台):方式二:使用uni

JAVA实现Token自动续期机制的示例代码

《JAVA实现Token自动续期机制的示例代码》本文主要介绍了JAVA实现Token自动续期机制的示例代码,通过动态调整会话生命周期平衡安全性与用户体验,解决固定有效期Token带来的风险与不便,感兴... 目录1. 固定有效期Token的内在局限性2. 自动续期机制:兼顾安全与体验的解决方案3. 总结PS

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

Python屏幕抓取和录制的详细代码示例

《Python屏幕抓取和录制的详细代码示例》随着现代计算机性能的提高和网络速度的加快,越来越多的用户需要对他们的屏幕进行录制,:本文主要介绍Python屏幕抓取和录制的相关资料,需要的朋友可以参考... 目录一、常用 python 屏幕抓取库二、pyautogui 截屏示例三、mss 高性能截图四、Pill

使用MapStruct实现Java对象映射的示例代码

《使用MapStruct实现Java对象映射的示例代码》本文主要介绍了使用MapStruct实现Java对象映射的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、什么是 MapStruct?二、实战演练:三步集成 MapStruct第一步:添加 Mave

Java抽象类Abstract Class示例代码详解

《Java抽象类AbstractClass示例代码详解》Java中的抽象类(AbstractClass)是面向对象编程中的重要概念,它通过abstract关键字声明,用于定义一组相关类的公共行为和属... 目录一、抽象类的定义1. 语法格式2. 核心特征二、抽象类的核心用途1. 定义公共接口2. 提供默认实