第六章 可以工作的类
类是由一组数据和子程序构成的集合,这些数据和子程序共同拥有一组内聚的、明确定义的职责。
类的基础:抽象数据类型(ADT)
抽象数据类型是指一些数据以及对这些数据所进行的操作的集合。
使用ADT的益处:可以隐藏实现细节、改动不会影响到整个程序、让接口提供更多信息、更容易提高性能、让程序的正确性更显而易见、程序更具自我说明性、无需在程序内到处传递数据、可以像在现实世界中那样操作实体,而不用在底层实现上操作它。
良好的类接口
好的抽象:
类的接口应展现一致的抽象层次;
一定要理解类所实现的抽象是什么;
提供成对的服务;
把不相关的信息转移到其他类中;
尽可能让接口编程,而不是表达语义;
谨防在修改时破坏接口的抽象;
不要添加与接口不一致的公用成员;
同时考虑抽象性和内聚性。
良好的封装:
尽可能地限制类和成员的可访问性;
不要公开暴露成员数据;
避免把私用的实现细节放入类的接口中;
不要对类的使用者做出任何假设;
避免使用有元类;
不要因为一个子程序里仅使用公用子程序,就把它归为公开接口;
让阅读代码笔写代码更方便;
要格外警惕从语义上破坏封装性;
留意过于紧密的耦合关系。
有关设计和实现的问题
包含(有一个)
通过包含来实现“有一个”的关系;
在万不得已时通过private继承来实现“有一个”的关系;
警惕有超过约7个数据成员的类
继承(是一个)
用public继承来实现“是一个”的关系;
要么使用继承并进行详细说明,要么就不要用它;
遵循Liskov替换原则;
确保只继承需要继承的部分;
不要覆盖一个不可覆盖的成员函数;
把共用的接口、数据及操作放到继承树中尽可能高的位置;
只有一个实例的类是值得怀疑的;
只有一个派生类的基类也是值得怀疑的;
派生后覆盖了某个子程序,但在其中没有做任何操作,这种情况也值得怀疑;
避免让继承体系过深;
尽可能使用多态,避免大量的类型检查;
让所有数据都是private(而非protected)。
成员函数和数据成员
让类中子程序的数量尽可能少;
禁止隐式地产生你不需要的成员函数和运算符;
减少类所调用的不同子程序的数量;
对其他类的子程序的间接调用要尽可能少;
一般来说,应尽量减小类和类之间相互合作的范围。
构造函数
如果可能,应该在所有的构造函数中初始化所有的数据成员;
用私用构造函数来强制实现单件属性;
优先采用深层复本,除非论证可行,才采用浅层复本。
创建类的原因
为实现世界中的对象建模;为抽象的对象建模;降低复杂度;隔离复杂度;隐藏实现细节;限制变动的影响范围;隐藏全局数据;让参数传递更顺畅;建立中心控制点;让代码更易于重用;为程序族做计划;把相关操作包装到一起;实现某种特定的重构。
应该避免的类
避免创建万能类;消除无关紧要的类;避免用动词命名的类。
创建类的理由
对象是世界中的对象建模;对抽象对象建模;降低复杂度;隔离复杂度;隐藏实现细节;限制变化所影响的范围;隐藏全局数据;让参数传递更顺畅;创建中心控制点;让代码更易于重用;为程序族做计划;把相关操作放到一起;实现特定的重构。
与具体编程语言相关的问题
下面列出跟类相关的,不同语言之间有着显著差异的一些地方:
在继承层次中被覆盖的构造函数和解析函数的行为;
在异常处理时构造函数和解析函数的行为;
默认构造函数的重要性;
解析构造函数或终结器的调用时机;
和覆盖语言内置的运算符相关的知识;
当对象被创建和销毁时,或当其被声明时,或者它所在的作用域退出时,处理内存的方式。
核对表:类的质量
抽象数据类型:
- [ ] 你是否把程序中的类都看作抽象类型了?是否从这个角度评估他们的接口了?
抽象
- [ ] 类是否有一个中心目的?
- [ ] 类的名字是否恰当?其名字是否表达了其中心目的?
- [ ] 类的接口是否展示了一直的抽象?
- [ ] 类的接口是否能让人清楚明白地知道该如何使用它?
- [ ] 类的接口是否足够抽象,使你能不必顾虑它是如何实现其服务的?你能把类看作的是黑盒子吗?
- [ ] 类提供的服务是否足够完整,能让其他类无需动用其内部参数?
- [ ] 是否已从类中出去无关信息?
- [ ] 是否考虑过把类进一步分解为组件类?是否已尽可能将其分解?
- [ ] 在修改类时是否维持了其接口的完整性?
封装
- [ ] 是否把类的成员的可访问性降到最小?
- [ ] 是否避免暴露类中的数据成员?
- [ ] 在编程语言所许可的范围内,类是否1⃣️尽可能地对其他的类隐藏了自己的实现细节?
- [ ] 类是否避免对其他使用者,包括其派生类会如何使用它做假设?
- [ ] 类是否不依赖于其他类?他是松散耦合的吗?
继承
- [ ] 继承是否只用来建立“是一个”的关系?也就是说,派生类是否遵循了LSP?
- [ ] 类的文档是否记述了其继承策略?
- [ ] 派生类是否避免了“覆盖”不可覆盖的方法?
- [ ] 是否把共用的接口、数据和行为都放到尽可能高的继承层次中了?
- [ ] 继承层次是否很浅?
- [ ] 基类中所有的数据成员是否都被定义为private而非protected的了?
跟实现相关的其他问题?
- [ ] 类中是否只有大约七个或更少的数据成员?
- [ ] 是否把类直接或间接调用其他类的子程序的数量减到最少了?
- [ ] 类是否只在绝对必要时才与其他的类相互协作?
- [ ] 是否在构造函数中初始化了所有数据成员?
- [ ] 除非拥有经过测量的、创建浅层复本的理由,类是否都被设计为当作深层复本的使用?
与语言相关的问题
- [ ] 你是否研究过所用编程语言里和类相关的各种特有的问题?