准备按照章节做一个类似索引一样的笔记,对于重点会做解释,一句话能懂的不会进一步解释。
第二章:有意义的命名
关键:有意义的命名,减少无意义的前缀后缀,能简明说明白的使用短命名,短的说明不清楚的使用使用长命名,
1.名副其实
2.不要使用操作系统中的专有名词,易引起误解
3.不要包含无意义内容,例如命名无需指明类型,name,nameString
4.少废话,和3类似,不要写the这种无意义内容
5.使用读得出来的名称
6.使用可搜索的名称,这里作者认为名称长短应该和作用域大小挂钩
7.减少前缀使用,例如MyCalculate,IShapeFactory。注意这里不是要求完全不能有前缀。如16
8.避免思维映射。不应当让读者脑中把你的名称翻译为他们熟知的名称。对一个单词的认知可能因为领域不同有不同理解习惯。
9.类名:名词&名词短语
10.方法名:动词&动词短语
11.别抖机灵
12.每个概念对应一个单词,例如:Info和data,应该固定一个使用,而不是混合使用。
13.别用双关语,避免将同一单词用于不同目的,同一术语用于不同概念,和12是相反的,避免出现一对多和多对一,尽量做到一一对应。
14.使用计算机科学术语
15.使用源自所涉及问题领域的名称。感觉和8比较容易冲突。
16.添加有意义的语境,如果在没有使用命名良好的类,函数,名称空间中。我们可以使用前缀添加语境.例如street,houseNumber,city,name这几个变量,很明显这是一个类似通讯录地址的语境,我们可以使用addrName,addrCity等来提供语境。当然更好的解决方法是创建Address类来存放变量。
第三章:函数
如何让函数写的符合下面的要求?打磨!而不是一开始就完全按照这个要求做,这样太难了。
1.函数要短小,且只做一件事情,如何确定是做一件事情?函数只做了在该函数名下的同一抽象层上的步骤,这里举个简单例子:
def check_password(user_name: str, user_pwd: str) -> bool:
......
if (......):
# 在只应该检查密码的位置,进行了调用,如果只看名字别人不知道会有一次初始化会话,造成了一次时序性耦合
Session.initialize()
2.每个函数一个抽象层级,如何理解抽象层级?个人理解抽象层级就是做的一件事情的粒度,例如做包子,这是一个较高的抽象层级,下一层的抽象层级就是和面,和馅料
3.要让代码拥有自顶向下的阅读顺序
4?switch语句,需要让switch埋藏在比较低的抽象层,例如将switch语句埋藏在抽象工厂底下(不是很理解)
5.使用具有描述性的名称
6.尽量减少函数参数的使用,确实除非有明确的注释,不然很多时候难以理解参数,当然实际情况中多参数太常见了而且不好修改,这个我觉得看看就行。不过这里作者认为如果参数过多我们应该尽量把这些参数封装为一个类。
7.不要传入标识参数,也就是bool类型的参数
8.尽量避免输出参数,当然这个在python中其实还好,毕竟可以return可以输出无限个内容.
9.使用异常代替使用错误码进行返回
10.剥离try/catch(except),try/catch打乱了代码结构,让正常流程中出现了错误处理,这也是我经常干的事情,从阅读的角度来说确实有所影响,所以作者认为最好可以把try/catch从主体中隔离开,错误处理就是一件事情!如下例子所示
# 此函数只有try/catch结构,具体的功能代码放到了外部
def delete(Page page):
try:
delete_page_and_all_references(page)
except Exception as e:
log_error(e)
def delete_page_and_all_references(Page page)
......
def log_error():
......
11.结构化编程,每个代码块只有一个入口一个出口,但是这样的效益并不大,适当的break,continue,return可以提高可读性或者提高速度。
第四章:注释
在书中,作者极力贬低注释,认为随着代码的变动,注释无法跟随着代码一起变动最终会导致无法正确解释代码。但是我认为注释依然是非常有用的解释代码的途径,对难以理解的代码是一种非常有力的补充。
1.注释不能美化代码
2.代码可读性不能使用注释来替代,要注重代码本身的阐述
3.注释除了提供有用信息,还可以提供某个决定后的意图,提供告警
4.定期检查TODO注释,删除不需要的TODO
5.注释可以用来放大某种看起来不合理事物的重要性
6.不是每个函数,变量都要有注释,不要循规式注释
7.不要有日志式注释
8.切勿废话,下栗子
class person():
# 初始化
def __init__(self, name, gender):
# 姓名
self.name = name
# 性别
self.gender = gender
9.能用函数或者变量的时候就不要使用注释
10.不要使用位置标记
11.不要注释代码!!!不需要的直接删除
12.不要HTML注释
13.不要在注释里写远离该注释位置的信息
14.不要在注释中写太多信息
第五章:格式
从让”代码能够工作“到让”好的代码风格延续“,这一章的一个重要内容就是“距离”,大体原则就是越相关的内容距离越近。
1.代码应该像报纸一样,顶部是高层次的概念和算法,向下逐渐展开细节
2.概念间垂直方向上区隔。不同逻辑,概念的代码通过分隔更容易看懂逻辑
3.关系紧密的代码应该靠近,例如类中的属性
4.变量声明应该尽可能靠近使用位置,因为函数很短,本地变量应该在函数的顶部出现
5.实体变量应该在类的顶部声明
6.相关函数尽量放在一起,调用者在被调用者上面,这个很重要,对于可读性有很大影响
7.横向代码应该尽可能少
8.水平方向上的区隔 同 垂直方向
9.尽量不要违反缩进规则,简短的代码同样可以需要缩进
10.空的while和for也需要缩进
11.遵从团队规则
第六章:对象和数据结构
书中提到了一个非常有意思的观点,是我之前没有思考的,大家总说面向过程是“过时的思想”,但是在书里面两个各有优劣(如1所示)。
1.“我们可以再次看到这两种(面向过程,面向对象)定义的本质,他们是截然对立的,这说明了对象和数据结构之间的二分原理:过程式代码(使用数据结构的代码)便于在不改动既有数据结构的前提下添加新函数,面向对象代码便于在不改动既有函数的前提下添加新类。”
2.得墨忒耳律(The Law of Demeter):模块不应了解它所操作对象的内部情形。
3.DTO:数据传送对象,这java里面经常用到,只有公共变量而没有函数的类。
4.bean:带有set/get方法的类。
第七章:错误处理
要保证错误处理不打乱代码逻辑。但是要把错误处理完全独立于逻辑之外还是很难啊。
1.使用异常而非返回码
2.先写Try-catch-finally,也就是我们常说的TDD?应该
3?使用未检异常
4.给出异常发生的环境说明
5.依调用者需要定义异常类
6.别返回null
7.别传递null
第八章:边界
关注的是使用三方件或者开源代码,要将这些代码干净利落的整合进自己代码,最近几章内容比较少,不是我水,是书确实内容就少哈哈哈哈。
1.如果使用 类似Map这样的边界接口,就把它保留在类或者近亲类中。避免从公共API中返回边界接口,或者将边界接口作为参数传递给公共API
2.编写测试来浏览和理解三方代码,这称之为学习性测试
3.使用尚不存在的代码需要小心,两种策略:第一确定好接口,第二可以使用Adapter模式
第九章:单元测试
编写好测试的一些细节和关键点
1.TDD三定律
1.1.在编写不能通过的单元测试前,不能编写生产代码
1.2.只可编写刚好无法通过的单元测试,不能编译也算不通过
1.3.只可编写刚好足以通过当前失败测试的生产代码
“这样编写代码,测试将覆盖所有生产代码,测试代码量足以匹配生产代码量,导致管理问题”
2.测试代码和生产代码一样重要,同样需要保持测试整洁,整洁的测试有3要素:可读性,可读性,可读性
3.每个测试一个断言,当然不是硬性要求,最重要的是单个测试的断言数量最小化
4.每个测试一个概念
5.整洁的测试应该遵循F.I.R.S.T规则
5.1.快速(Fast),测试运行速度快
5.2.独立(independent),测试之间应该相互独立
5.3.可重复(repeatable),测试应该可以在任何环境中重复通过
5.4.自足验证(self-validating),测试应该有布尔值输出
5.5.及时(timely),测试应该及时编写
第十章:类
需要将注意力放到代码组织的更高层,否则无法得到简洁的代码
1.类应该短小,对于函数我们使用代码行数来衡量其大小,对于类我们使用计算权责(responsible)的方法。
2.单一权责原则(SRP):类或者模块应该有且只有 一条加以修改的理由
3.内聚:类中的越多变量,被越多的类中方法使用,内聚程度就越高。每个变量都被每个方法使用,称该类具有最大的内聚性。内聚性越高越能表示这是一个整体。
4.保持内聚性就会得到许多短小的类
5.依赖倒置原则(DIP):类应当依赖抽象而不是具体的实现
第十一章:系统
这一部分好多没看懂,书中的内容很多,但是我总结不出来,推荐直接看书学习。
1.将系统的构造和使用分开,一个典型的错误情况:延迟初始化/赋值
2.分解main,将构造和使用功能分开的一种方法就是,将所有的构造全部放到main中,然后通过main传递给应用程序。
3.使用抽象工厂类,是第二种方式。
4.方式三,使用依赖注入(DI),DI是控制反转(IoC)在依赖管理中的一种应用手段。
5.扩容:面向切面编程(AOP)
6.Java代理,纯java AOP模式,AspectJ
7.测试驱动系统架构
8.明智使用添加了可论证价值的标准
9.领域特定语言(DSL),DSL是一种单独的小型脚本语言或者以标准语言写就的API
第十二章:迭进
本章主要阐述简单设计规则的四条规则
1.运行所有测试,老生常谈
2.重构代码之后,重新审视一下设计,设计是否退步了,如果退步了就要清理并且运行测试,保证没有破坏任何东西。
3.不可重复
4.良好的表达力
5.尽可能少的类和方法,注意这个优先级最低,更重要的是测试,消除重复和表达力。
第十三章:并发编程
本书,作者只是概述性的说明了并发编程的注意事项。且这一章主要是根据java语言来说明,没怎么写过并发可能掌握不到重点。
1.单一权责原则,建议:分离并发相关代码和其他代码
2.限制数据作用域,建议:谨记数据封装,严格限制对可能被共享的数据的访问
3.使用数据副本
4.线程应尽可能地福利,建议:尝试将数据分解为可被独立线程(可能在不同处理器上)操作的独立自己
5.避免使用一个共享对象的多个方法
6.保持同步区域微小
7.关于测试代码的建议:
7.1.将伪失败看做可能的线程问题,不要将系统错误归咎于偶发事件
7.2.先使非线程代码可工作,不要同时跟踪非线程缺陷和线程缺陷,保证代码在线程之外可工作
7.3.编写可拔插&调试的线程代码,可在不同配置环境下运行
7.4.运行多于处理器数量的线程,任务交换越频繁越容易找到临界区或者导致死锁的代码
7.5.在不同平台上运行
7.6.调整代码并强迫错误发生
7.7.可以使用硬编码&自动化来测试代码
第十四章:逐步改进
书中用了大概50页来讲述一个程序的改进案例,略
第十五章:JUnit
讲述了一个JUnit框架的代码改进案例,略
第十六章:重构SerialDate
重构JCommon类库中的org.jfree.date的SerialDate类,略
第十七章:味道与启发
本章是总结性质章节,并且联动了《重构:改善既有代码设计》,其中大部分内容前面十三章都已经讲过了,等看完了重构再来更新这个吧。