第6章 可以工作的类
最后更新于:2022-04-01 05:01:35
## 第6章 可以工作的类
> // checklist For Design in Construction
> 核对表:软件构造中的设计
### 设计实践
- 你已经做过多次迭代,并且从众多尝试结果中选择最佳的一种,而不是简单选择第一次尝试的结果吗?
- 你尝试用多种方案来分解系统,以确定最佳的方案吗?
- 你同时用自上而下和自下而上的方法来解决设计问题吗?
- 为了解决某些特定的问题,你对系统中的风险部分或者不熟悉的部分创建过原型、写出数量最少的可抛弃的代码吗?
- 你的设计方案被其他人检查了吗(无论正式与否)?
- 你一直在展开设计,知道实施细节跃然纸上吗?
- 你用某种适当的技术—比如说Wiki、电子邮件、挂图、数码相机、UML、CRC卡或者在大马鞋注释–来保留设计成果吗?
### 设计目标
- 你的设计是否充分地处理了由系统架构层定义出并且推迟确定的事项吗?
- 你的设计被划分为层次吗?
- 你对把这一程序分解成为子程序、包和类的方式感到满意吗?
- 你把对这个类分解成为子程序的方法感到满意吗?
- 类与类之间的交互关系是否已设计成最小化了?
- 类和子程序是否被设计为能够在其他的系统中重用?
- 程序是不是易于维护?
- 设计是否精简?设计出来的每一部分都是绝对必要的吗?
- 设计中是否采用了标准的技术?是否避免使用怪异且难以理解的元素?
- 整体而言,你的设计是否有助于最小化偶然性和本质性的复杂度吗?
> 6.1 类的基础:抽象数据类型(ADTs)
### 使用ADT的益处
- 可以隐藏实现细节
- 改动不会影响到整个程序
- 让接口能提供更多的信息
- 更容易提高性能
- 让程序的正确性更加显而易见
- 程序更具有自我说明性
- 无须在程序内到处传递数据
- 你可以像在现实世界中那样操作尸体,而不用再底层实现上操作它
> 良好的类接口
### 好的抽象
- 类的接口应该展现一致的抽象层次?
- 一定要理解所实现的抽象是什么?
- 提供成对的服务?
- 把不相关的信息转移到其他类中?
- 尽可能让接口可编程,而不是表达语义?
- 谨防在修改时破坏接口的抽象?
- 不要添加于接口抽象不一致的公用成员?
- 同时考虑抽象性和内聚性?
> Good Encapsulation
### 良好的封装
- 尽可能地限制类和成员的可访问性?
- 不要公开暴露成员数据?
- 避免吧私用的实现细节放入类的接口中?
- 不要对类的使用者作出任何假设?
- 避免使用友元类?
- 不要因为一个子程序里仅使用公用子程序,就把它归入公开接口?
- 要格外警惕从语义上破坏封装性?
- 留意过于紧密的耦合关系?
> Containment (“has a” Relationships)
### - 包含(“有一个…”的关系)
- 通过包含来实现“有一个/has a ” 的关系?
- 在万不得已时通过private 继承来实现“有一个”的关系?
- 警惕有超过7个数据成员的类?
> Inheritance (“is a ” Relationship)
### 继承(“是一个…”关系)
- 用public 继承来实现”是一个“的关系?
- 要么使用继承并进行详细说明,要么就不要使用它?
- 遵循Liskov替换原则?
- 确保只继承需要继承的部分?
- 不要”覆盖“一个不可覆盖的成员函数?
- 把共用的接口、数据及操作放到继承树种尽可能高的位置?
- 只有一个实例的类是值得怀疑的?
- 只有一个派生类的基类也是值得怀疑的?
- 派生后覆盖了某个子程序,但在其中没有做任何操作,这种情况也是值得怀疑的?
- 避免让继承体系过深?
- 尽量使用多态,避免大量的类型检查?
- 让所有数据都是private ,而非protected
> Why Are There So Many Rules for Inheritance
### 为什么有这么多关于继承的规则?
- 如果多个类共享数据而非行为,应该创建这些类可以包含的共用对象?
- 如果多个类共享行为而非数据,应该让它们从共同的基类继承而来,并在基类里定义共用的子程序?
- 如果多个类即共享数据也共享行为,应该让它们从一个共同的基类继承而来,并在基类里定义共用的数据和子程序?
- 当你想由基类控制接口时,使用继承;当你想自己控制接口时,使用包含?
> Reasons to Create a Class
### – 创建类的原因
- 为现实世界中的对象?
- 为抽象的对象建模?
- 降低复杂度?
- 隔离复杂度?
- 隐藏实现细节?
- 限制变动的影响范围?
- 隐藏全部数据?
- 让参数传递更顺畅?
- 建立中心控制点?
- 让代码更加容易复用?
- 为程序族做计划?
- 把相关操作包装到一起?
- 实现某种特定的重构?
> checklist For Class Quality
### 核对表:类的质量
### 抽象数据类型
1. 你是否把程序中的类都砍做事抽象数据类型了?是否从这个角度评估它们的接口?
### 抽象
- 类是否有一个中心目的?
- 类的命名是否恰当?其名字是否表达了其中心目的?
- 类的接口是否展现一致的抽象?
- 类的接口是否足够抽象,使你能不必顾虑它是如何实现其服务的?你能把类看做黑盒子吗?
- 类提供的服务是否足够完整,能让其他类无须动用其内部数据?
- 是否已从类中除去无关信息?
- 是否考虑过把类进一步分解为组件类?是否已尽可能将其分解?
- 在修改类是是否维持了其接口的完整性?
### 封装
- 是否把类的成员的可访问性降到最小?
- 是否避免暴露类中的数据成员?
- 在编程语言所许可的范围内,类是否尽可能的对其他的类隐藏自己的实现细节?
- 类是否避免对其使用者,包括其派生类会如何使用它做了假设?
- 类是否不依赖于其他类?它是松散耦合的吗?
### 继承
- 继承是否只用来建立”是一个“的关系?也就是说,派生类是否遵循了LSP原则?
- 类的文档中是否记述了其继承策略?
- 派生类是否避免了”覆盖“不可覆盖的方法?
- 是否把公用的接口、数据和行为都放到尽可能高的继承层次中了?
- 继承层次是否很浅?
- 基类中所有的数据成员是否都被定义为private而非protected的了?
### 跟实现相关的其他问题
- 类中是否只有大约7个或者更少的数据成员?
- 是否把类直接或间接调用其他类的子程序的数量减到最少了?
- 类是否只在绝对必要时才与其他的类相互协作?
- 是否在构造函数中初始化了所有的数据成员?
- 除非拥有经过测量的、创建浅层副本的理由,类是否都被设计为当做深层副本使用?
### 与语言相关的问题
- 你是否研究过所用编程语言里和类相关的各种特定问题?
### 中文要点:
- 类的接口应提供一致的抽象。很多问题都是由于违背该原则而引起的。
- 类的接口应隐藏一些信息――如某个系统接口、某项设计决策、或一些实现细节。
- 包含往往比继承更为可取――除非你要对“是一个/is a”的有关系建模。
- 继承是一种有用的工具,但它却会增加复杂度,这有违于软件的首要技术使命――管理复杂度。
- 类是管理复杂度的首选工具。要在设计类时给予足够的关注,才能实现这一目标。
### English Key Points:
- Class interfaces should provide a consistent abstraction. Many problems arise from violating this single principle.
- A class interface should hide something—a system interface, a design decision, or an implementation detail.
- Containment is usually preferable to inheritance unless you’re modeling an “is a” relationship.
- Inheritance is a useful tool, but it adds complexity, which is counter to the Primary Technical Imperative of minimizing complexity.
- Classes are your primary tool for managing complexity. Give their design as much attention as needed to accomplish that objective.