C++API设计 读书笔记
API 设计
简介:
API 是一个明确定义的接口,可以为其他软件提供特定服务。
范围:小到一个函数,一个类。大到一个类库。
特征:
2.1 问题域建模:
2.1.1 提供良好的抽象
在设计API时,应该阐述在选定问题域内有意义的深层概念,而不是公开底层实现细节。
2.2 隐藏实现细节:
2.2.3 隐藏成员变量
使用get/set 的好处:有效性验证,惰性求值,缓存,额外的计算,set方法内通知,精细的访问控制
2.2.4 隐藏实现方法
强烈建议在API中采用Pimpl惯用法,这样就可以将所有实现细节完全和公有头文件分开。如果你不想这么做,
将私有功能声明为cpp文件中的静态函数,而不要将其作为私有方法暴露在公开的头文件中。
2.2.5 隐藏实现类
2.3 最小完备性—尽量简洁,但不要过分简洁
2.3.1 不要过度承诺
客户使用后增加新功能很容易,而移除功能就非常困难。当不确定是否需要某个接口时,就不要提供此接口。
工程师容易被解决方案的通用性和灵活性诱惑,所以可能情不自禁地为API增加抽象层次或通用性。
工程师应该抵制这种诱惑,原因如下:
1.你想要添加的通用性可能永远不会用到。
2.如果某天用到了想要添加的通用性,那时你已有了与最初设想方案不同的解决方案。
3.确实需要添加新功能,简单的API远比复杂的API更容易添加新功能。
2.3.2 谨慎使用虚函数
没有虚函数的类比有虚函数的类更健壮更易于维护。

2.3.4 便捷API
简化API 函数数目与API易于各种客户使用之间有矛盾。一方面,有人认为API应该仅提供一个方法,仅执行一项任务,
这确保了API是最小化的,集中的,一致的,易于理解的,还减少了实现的复杂性,更稳定,更易于调试,总的来说
API具有原语性。另一方面,也有人认为API应该让简单的事情更简单,不应该要求客户编写大量代码完成基础任务。
两个目标都是合理的。解决方案是不要将便捷API与核心API混在同一个类中。也就是说,应该创建补充类包装核心API
的某些公有功能。便捷类应该与核心API完全隔离,放在不同的源文件甚至独立的库中。

2.4 易用性
2.4.2 不易误用,用 枚举代替多个bool

2.4.3 一致性
使用一致的函数命名和参数顺序
2.4.4 正交
意味着没有副作用。调用设置特定属性的方法,不能额外改变其他可以公共访问的属性。
减少冗余,增加独立性。
2.4.5 健壮的资源分配
提供资源的接口,和释放资源的接口成对提供。
2.4.6 平台独立

2.5 松耦合—C++的耦合可以通头文件的include 程度看出来
2.5.1 仅通过声明解耦合
2.5.2 降低类耦合
基于公有接口实现的功能都声明在类的外部。类似于便捷API。可以将新的API放在如MyObjectHelper 的命名空间。
2.5.3 刻意的冗余
有时使用数据冗余降低类之间的耦合是合理的,应该小心谨慎使用。
2.5.4 管理器类
2.5.5 回调,观察者和通知
2.6 稳定的、文档详细且经过测试的API
模式

3.1 pimpl 惯例法(point to implementation)
该技巧支持在公有接口中完全隐藏内部细节。从本质上讲,它支持将私有的成员数据和方法转移到cpp文件中,达到接口和实现分离。


Impl类中放置多少逻辑。通常推荐方法a。注意impl不要调虚函数。
a. 私有成员变量和方法
b. 公有类的所有方法,其中公有方法只是对impl 类中等价方法的包装
Pimple的优点:
a. 隐藏信息
b. 降低头文件的耦合
c. 加速编译
d. 更好的二进制兼容性 ABI
e. 惰性分配资源
Pimpl的缺点:
a. 性能
b. 繁琐
c. 编译器仅检查const 方法中的impl 指针,不检查指针指向成员的变化
3.4 API 包装器模式
场景:a. 维护大型遗留代码库,设计新的api,隐藏遗留代码
b. 已经编写了一个C++API,给特定客户提供纯C API
c. 隐藏第三方依赖库
3.4.1 代理模式 - 惰性实例化原始对象
3.4.2 适配器模式 - 强制API始终保持一致性
3.4.3 外观模式 - 创建便捷API(一般以单例的形态在生命周期里)
3.5 观察者模式
3.5.1 MVC 架构模式要求业务逻辑(Model,模型)独立于用户界面(view,视图),
控制器(controller)接收用户输入并协调另外两者。MVC支持应用程序的功能模块化,并具有以下优点:
a. 模型和视图组件的隔离,就可以实现多个用户界面,并且使这些界面能够重用公共的业务逻辑
b. 避免了因为多分UI实现而构建多份重复的底层模型代码的问题
c. 模型和视图代码的解耦简化了核心业务逻辑的单元测试
d. 组件的模块化允许核心逻辑的开发和GUI开发者并行工作,且互不影响

3.5.2 实现观察者模式
观察者模式引入两个概念:主题(Subject)和观察者(Observer),也叫做发布者和订阅者。
多个观察者注册主题中的事件,之后主题会在自身状态发生变化时通知所有注册的观察者。
3.5.3 推与拉观察者

4 设计
4.6 类的设计-集中精力设计定义API 80% 功能的 20% 的类
4.6.2 类设计选项
继承
组合
抽象接口
标准设计模式
初始化析构模型
复制构造函数,赋值操作符
模版的使用
const ,explicit
定义操作符
4.6.6 迪米特法则 - 最少知识原则
一个函数只可以做下面几条事情:
a. 调用同一个类的其他函数
b. 在同一个类的数据成员上调用函数
c. 在它接受的任何参数上调用函数
d. 在它创建的局部对象上调用函数
e.g: 避免m_obja.getObjB().doSomthing()
4.6.7 类的命名
简单的类名应该描述性强,自解释的。比如Customer,bookmark,设计模式术语。
使用专业术语
接口往往使用对象模型中的形容词表示。比如IRenderable,IObservable
有命名空间
4.7 函数设计
a. 是否静态
b. 参数是值传递、const引用、指针
c. 参数是否const
d. 是否有默认参数
e. 结果是值,const 引用,还是指针
f. 是否是虚函数
g. 是否是纯虚函数
h. const 成员函数
i. 非默认构造函数使用explicit
j. 是否内联函数
4.7.2 函数命名
函数名不应该以 _ 开头,编译器预定了

浙公网安备 33010602011771号