代码大全读书笔记一
6.4:创建类的理由
- 对现实世界中的对象建模
- 对抽象对象建模
- 降低复杂度
- 隐藏实现细节
- 限制变化锁影响的范围
- 隐藏全局数据
- 让参数传递更顺畅
- 创建中心控制点
- 让代码更易于重用
- 为程序族做计划
- 把相关操作放到一起
- 实现特定的重构
6.4.1:避免创建的类:
- 避免创建万能类
- 消除无关紧要的类
- 避免用动词命名的类。
6.6.1:类的质量
- 你是否把程序中的类都看做是抽象的数据类型了?是否从这个角度去评估他们的接口了?
- 类是否有一个中心目的?
- 类的命名是否恰当,其名字是否表达了其中心目的
- 类的接口是否直线了一致的抽象
- 类的接口是否能让人清楚明白地知道该如何使用它?
- 类的接口是否足够抽象,使你能不必顾虑它是如何实现其服务的?你能把类看做是黑盒子吗?
- 类提供的服务是否足够完整,能让其他类无须动用其内部数据?
- 是否已从类中除去无关信息?
- 是否考虑把类进一步分解为组件类?是否已经尽可能将其分解?
- 在修改类时是否维持了其接口完整性?
- 是否把类的成员的可访问性降到最低?
- 是否避免暴露类中的数据成员?
- 在编程语言所允许的范围内,类是否已经尽可能地对其他类隐藏了自己的实现细节?
- 类是否依赖于其他类?他是松耦合的吗?
- 继承是否用来建立“是一个/is a”的关系?也就是说是否遵循了LSP(Liskov替换原则)?
- 类的文档中是否记述了其继承策略?
- 派生类是否避免了“覆盖”不可覆盖的方法?
- 是否把公用接口、数据和行为都放到了尽可能高的继承层次中了?
- 继承层次是否很浅
- 基类中所有数据成员是否被定义为private而非protected的了?
- 类是否只有大约7个或者更少的数据成员
- 是否把类直接或者间接调用了其他类的子程序的数量减到最少了?
- 类是否在绝对必要时才与其他的类相互协作?
- 是否在构成函数中初始化了所有的数据成员?
- 除非拥有经过测量的、创建浅层复本的理由,类是否被设计为当着深层复本使用了?
- 你是否研究过所用编程语言里和类相关的各种特有的问题?
要点:
- 类的接口应提供一致的抽象。很多问题都是由于违背了该原则而引起的。
- 类的接口应该隐藏一些信息,如某个系统的接口、某项设计决策、或者一些实现细节。
- 包含往往比继承更为可取,除非你要对“是一个/is a”的关系建模
- 继承是一种有用的工具,但它会增加复杂度,这有违于软件的首要技术使命—管理复杂度。
- 类是管理复杂度的首选工具。要在设计类时给予足够的关注,才能实现这个目标。
7.1:创建子程序的理由
- 降低复杂度
- 引入中间的易懂的抽象
- 避免代码重复
- 支持之类化
- 隐藏顺序
- 隐藏指针操作
- 提供可移植性
- 简化复杂逻辑的判断
- 改善性能
- 隔离复杂度
- 隐藏实现细节
- 限制变化所带来的影响
- 隐藏全局数据
- 形成中央控制点
- 促成可重用代码
- 达到特定的重构目的
7.7:高质量的子程序
- 创建子程序的理由充分吗?
- 一个子程序中所有适于单独提出的部分是不是已经被提出到单独的子程序中了?
- 过程的名字是否用了强烈、清晰的“动词+宾语”词组?函数的名字是否描述了其返回值?
- 子程序的名字是否描述了其所做的全部事情?
- 是否给常用的操作建立了命名规则?
- 子程序是否具有强烈的功能上的内聚性?即他是否做且只做一件事,并且把它做好?
- 子程序之间是否有较松的耦合?子程序和其他子程序的连接是否是小的、明确的、可见的、灵活的?
- 子程序的长度是否由其逻辑自然确定,而非遵循任何人的编码标准?
参数传递事宜:
- 整体来看,子程序的参数表是否表现出一种具有整体性且一致的接口抽象?
- 子程序的参数的排序规则是否合理?是否与类似的子程序的参数排序相符?
- 接口假定是否已在文档中说明?
- 子程序的参数有没有超过7个?
- 是否用到了每一个输入参数?
- 是否用到了每一个输出参数?
- 子程序是否避免了把输入参数用做工作变量?
- 如果子程序是一个函数,那么它是否在所有情况下都能返回一个合法值?
要点:
- 创建子程序最主要的目的是提高程序的可管理性,当然也有其他一些好的理由。其中节省代码空间只是一个次要原因;提高可读性、可靠性和可修改性等原因都更重要。
- 有时候,把一些简单的操作写成子程序也非常有价值
- 子程序可以按照内聚性分为很多类,而你应该让大多数子程序具有功能上的内聚性,这是最佳的一种内聚性。
- 子程序的名字是其质量的指示器。如果名字糟糕但恰如其分,那么说明这个子程序设计得很差劲。如果名字糟糕而且又不准确,那么它就是反应不出程序是干什么的。不管怎样,糟糕的名字都意外着程序需要修改。
- 只有在某个子程序的主要目的是返回由其名字所描述的特定结果时,才应该使用函数。
- 细心的程序员会非常小心的使用宏,而且只有万不得已才使用。
8.8防御式编程
一般事宜:
- 子程序是否保护自己免遭有害输入数据的破坏?
- 你用断言来说明编程假定吗?其中包括了前条件和后条件吗?
- 断言是否只是用来说明不应该发生的情况?
- 你是否在架构或者高层设计中规定了一组特定的错误处理技术?
- 你是否在架构或者高层设计中规定了让错误处理倾向于健壮性还是正确性?
- 你是否建立了隔栏来遏制错误可能造成的破坏?是否减少了其他需要关注错误处理的代码的数量?
- 代码中用到了辅助调试的代码吗?
- 如果需要启用或者禁用辅助调试的是否无须大动干戈?
- 在防御式编程时引入代码量是否适宜----即不过多,也不过少?
- 你在开发时是否使用了进攻式编程了使错误难以被发现?
异常:
- 你在项目中定义了一套标准化的异常处理方案了吗?
- 是否考虑过异常处理之外的其他替代方案?
- 如果可能的话,是否在局部处理了错误而不是把它当成一个异常抛到外部?
- 代码中是否避免了在构造函数和析构函数中抛出异常?
- 所有的异常是否都抛出了他们的子程序所处的同一抽象层次上?
- 每个异常是否都包含了关于异常所发生的背景信息?
- 代码中是否没有使用空的catch语句? 如果使用了空的catch语句合适吗?能明确说明吗?
安全事宜:
- 检查有害输入数据的代码是否检查了故意的缓冲区溢出、sql注入、html注入、整数溢出以及其他恶意输入数据?
- 是否检查了所有的错误返回码?
- 是否捕获了所有的异常?
- 出错消息中是否避免出现现有的有助于攻击者攻入系统所需要的信息?
要点:
- 最终产品代码中对错误的处理方式要比 “垃圾进、垃圾出”复杂得多。
- 防御式编程可以让错误更容易发现、更容易修改、并减少错误对产品代码的破坏。
- 断言可以帮助人们尽早的发现错误,尤其是在大型系统和可考性系统中,以及快速变化的代码中。
- 关于如何处理错误输入的决策是一项关键的错误处理决策,也是一项关键的高层设计决策。
- 异常提供了一种与代码正常流程角度不同的错误处理手段。如果留心使用异常,他可以成为程序员们知识工具箱中的一项有益补充,同时也应该在异常和其他错误处理手段之间进行权衡比较。
- 针对产品代码的限制并不适用于开发中的软件。你可以利用这一优势在开发中添加有助于排查错误的代码。