目录
第六章:可以工作的类
6.1类的基础:抽象数据类型
抽象数据类型(ADT,abstract data type)是指一些数据以及对这些数据所进行的操作的集合。
6.2良好的类接口
如何设计类接口
- 类的接口应该展现一致的抽象层次:每个类应该实现一个ADT,并且仅实现这个ADT。
- 一定要理解类锁实现的抽象是什么:一些类非常相像。你必须非常仔细地理解类的接口应该捕捉的抽象到底是哪一个
- 提供成对的服务:一些子程序的功能天生就具有相对性,考虑要不要实现相对的功能取决于需求
- 把不想管的信息转移到其他类中:还是类的专一性的提现。
- 尽可能让接口可编程而不是表达语义
- 防止在修改的时候破坏接口的抽象
- 不要添加与接口抽象不一致的公用成员
- 同时考虑抽象性和内聚性
良好的封装
抽象通过提供一个可以让你忽略实现细节的模型来管理复杂度,而封装则强制组织你看到细节。
- 尽可能限制类和成员的可访问性:让可访问性尽可能低是实现封装的基本原则
- 不要公开暴露成员数据
- 避免把私用的实现细节放入类的接口中
- 不要对类的使用者做出任何假设,要保证无论如何使用这个类都是可行的
- 不要因为子程序使用了公用子程序就把它归入公开接口
- 让阅读代码比编写代码更方便
- 避免从语义上去破坏封装性
每当你发现自己是通过查看类的内部实现来得知该如何使用这个类的时候,你就不是在针对接口编程了,而是在透过接口针对内部实现编程了。
- 留意过于紧密的耦合
- 尽量限制类和成员的可访问性,在基类中把数据申明成private而不是protected,降低派生类和基类的耦合
- 避免在类的公有接口中暴露成员数据
- 避免语义上破坏封装性
6.3有关设计和实现的问题
类的设计包含俩方面:
- 定义合理的接口
- 类内部的设计与实现
包含关系(有一个...的关系)
继承关系(是一个...的关系)
继承的目的在于,通过“定义能为俩个或更多派生类提供公有元素的基类”的方式,写出更精简的代码
应该考虑的事:
- 成员函数对派生类的可见性,是否应该有默认实现,是否可覆盖
- 数据成员对派生类可见吗
注意事项:
- 要么使用继承并说明,要么就不用
- 遵循里氏替换原则
- 不要在子类中有和基类重名的函数(不是覆盖)
- 只有一个实例的类要斟酌
- 只有一个派生类的基类也要斟酌
- 避免让继承体系过深
- 尽量使用多态(动态多态,此处不包含函数重载代表的静态多态)
- 让所有的数据都是private,如果需要去取数据通过public或者protected访问器实现
何时使用继承:
- 如果多个类共享数据而非行为,则把数据抽出来变成数据对象,类去包含数据对象
- 如果多个类共享行为而非数据,则让他们从共同基类继承而来,在基类中定义公用子程序
- 如果多个类共享数据和行为,那么就在基类里实现这些共享的部分然后继承而来
- 当想要由基类控制接口时,使用继承,当想自己控制时,使用包含
成员函数和数据成员
- 子程序数尽可能少
- 减少类中调用其他类子程序的数量
- 对其他类子程序的间接调用尽可能少,例如a.b.c()
- 减少类与类之间的耦合
构造函数
- 优先使用深层拷贝而不是浅拷贝(未理解)
6.4创建类的原因
- 为现实世界中的物体(对象)建模。
- 为抽象的概念建模
- 降低复杂度
- 隔离复杂度
- 隐藏实现细节
- 限制程序变动影响范围
- 参数传递更顺畅
- 建立中心控制点
- 代码重用
- 把相关操作包装在一起
- 实现某种特定重构
第七章:高质量的子程序
7.1 创建子程序的理由
- 降低复杂度
- 引入中间,易懂的抽象
- 避免代码重复
支持了子类的覆盖
- 隐藏了代码内部的执行顺序
- 隐藏指针操作
- 提高可移植性
- 简化复杂的布尔判断
- 改善性能方便
- 能让每个子程序都处于合适的长度
过于简单的子程序存在的作用
- 自我注释
- 提供扩展性,可能会出现简单程序变复杂的需求
7.2在子程序层上设计
应该有的内聚性:
- 功能的内聚性
不应该有的内聚性
- 顺序上的内聚性
- 通信上的内聚性:一个子程序中不同操作使用了相同的数据,可以考虑拆分成小的子程序,然后合并到大的子程序中,使得每个子程序都符合功能内聚
- 临时的内聚性:比如一个流程控制函数里包含了各不相关的子程序,这种常常是可以的,因为可以把这个流程理解为一个功能
- 过程上的内聚性:要对过程内聚进行分解,变成功能内聚
- 逻辑上的内聚性:若干操作放入同一个子程序,通过传入的控制标志选择执行其中一项,但是这种有一个特例,就是只进行发布命令的子程序,也就是“事件处理器(event handler)”。
- 巧合的内聚性:也就是没有内聚,硬凑的
7.3好的子程序名字
- 描述子程序所做的所有事:自明
- 避免使用无意义,表述不清的动词
- 不仅仅用数字命名
- 根据需要确定子程序长度
- 对返回值有一定的描述
- 使用对仗词
- 为常用操作确定命名规则
7.4子程序可以写多长
包含复杂算法的子程序 200内
一般子程序:50内
7.5如何使用子程序参数
- 按照输入-修改-输出的顺序排列参数
- 如果几个子程序都用到了类似的一些参数,那要保持参数顺序一致
- 使用所有参数
- 把状态变量放到最后
- 不要把子程序的参数用作工作变量,要自己取一个局部变量去进行工作计算
- 在接口中对参数的假定进行说明
- 子程序的参数个数限制在7个以内
- 为子程序传递能够维持其接口抽象的变量或者对象
- 使用具名参数
7.6使用函数时要特别考虑的问题
什么时候使用函数(有返回值)什么时候使用过程(void)
一个子程序的主要用途就是返回由其名字所指明的返回值,那么就应该使用函数,否则就应该使用过程。