《C++API 设计》4.6 类设计
摘抄于《C++API设计》4.6
类设计经验
80/20法则,把精力放在 定义了系统80%行为的20%的类上。
类设计上的选项:
- 继承的使用:考虑类放在 现有继承层次结构中是否合适?公有继承还是私有继承?
- 组合的使用:考虑 相对于继承,将关联的对象 作为 数据成员 是否 更合适?
- 抽象接口的使用:设计抽象基类,子类重写各个纯虚成员函数?
- 标准设计模式的使用:在类设计中采用 众所周知的设计模式
- 初始化与析构模型:对象创建与删除,由客户通过new/delete实现?工厂方式管理?使用智能指针?
- 定义复制构造函数和赋值操作符
- 模板的使用:设计的类定义了一族的类型还是一种?
- const 和 explicit的使用。参数、返回值、方法尽量定义为const。使用explicit 防止隐式类型转换
- 是否需要定义操作符
- 是否需要定义类型转换操作符
- 友元的使用:会破坏类的封装,友元的出现往往是设计变坏的征兆
- 非功能性约束:性能、内存的使用情况约束
使用继承的建议:
- 明确 设计上支持还是禁止继承:如果支持,需要深入考虑哪些方法应该定义为 虚函数,并将行为记录在文档中。如果不支持,应将 其析构函数 声明为非虚
- 仅在适当的地方使用继承:Liskov替换原则(LSP)
- 避免深度继承树:深度继承结构增加设计复杂性,任何多于两层,三层 都是复杂的。
- 使用纯虚成员函数强制子类提供实现
- 不要为现有接口增加新的纯虚函数:会破坏客户代码
- 不要过度设计:一个好的API是最小完备的,如 整个api中只有一个类继承它,对当前系统的需求而言 就是过度设计
Liskov替换原则:
用于指导一个类是否应该设计为另一个类的子类。指出 如果S是T的子类,那么行为上,S类型的对象不需要修改 就能 替换 T类型的对象。
组合 优先于 继承:
开闭原则OCP:
- 为扩展而开发、为修改而关闭
- 理念:一旦一个类创建完成并已向用户发布,唯一可以修改的只有编程错误。新特性的添加或功能的修改 应该通过 创建新类的方式实现
但是,在实际的软件项目中OCP难以实现,系统发布后源代码永远不修改这一约束往往是不现实的,此外,任何行为变化都有创建新类这一规定 会 破坏原本清晰且最小化的设计。
因此OCP是一种 启发式的指导原则,而非必须遵守的原则。
可以将OCP重新理解为:类的接口为变化而关闭,而非接口背后的精确实现 不可改变。
API 应该为不兼容的接口变化而关闭,为功能扩展而开发。
迪米特法则:
又叫做最少知识原则。
指出:每个组件对其他组件 都只有有限的知识,甚至只知道与其紧密相连的组件的知识。意味着一个函数 只能做以下几个方面:
1.调用同一个类的其他函数
2.在同一个类的数据成员上调用函数
3.在它接受的任何参数上调用函数
4.在它创建的任何局部对象上调用函数
5.在全局对象上调用函数(不应该有全局变量)
错误例子:
迪米特法则指出,应该只调用自己类或直接相关对象的函数