C++20高级编程 第六章 设计可重用代码
第六章 设计可重用代码
重用哲学
- 编写一次,经常使用
- 不惜一切代价避免代码重复
- DRY(Don't Repeat Yourself)
(不要重写自己写过的代码) - 代码不大可能只在一个程序中使用
(因而应该正确设计好接口与结构) - 团队中其他程序员必须也能使用你的代码
"客户"一般指使用接口的程序员,"用户"一般指使用程序者
C++设计两个原则
- 抽象: 设计函数与类,使得自己和其他程序员可以通过接口使用它们,而不需要知道底层实现.
- 重用: 编写适合重用的代码,一次编写,多次使用.
库: 是用于完成特定任务或者针对特定领域的代码集合.
框架: 是代码的集合,围绕框架设计程序.例如,微软基础类(Microsoft Foundation Classes,MFC)与Qt
API: 应用程序编程接口,为库或代码为特殊目的提供的接口.
原型: 首次使用某个新库或框架时为了测试库功能与性能的程序.
使用抽象
抽象 的关键是有效地将接口与实现分离.
实现是用来完成任务的代码,接口是其他用户使用代码的方式.
一个好的接口应该只包含公共方法.
类的属性永远不应该公开,但是可以通过获取器与设置器公开.
当设计接口时,不要向客户公开实现细节.
有时为了将某个接口返回的信息传递给其他接口,库要求客户代码保存这些信息.
这一信息有时被称为 句柄 ,用来跟踪某些特别的实例.
构建重用代码的规则
- 避免组合不相干的概念或者逻辑上独立的概念(高内聚)
- 将程序分为逻辑子系统(低耦合)
- 用类层级结构分离逻辑概念
- 用聚合分离逻辑概念
- 消除用户界面依赖
系统设计步骤
- 将系统分割为子系统
模型-视图-控制(MVC)模式:许多应用程序经常要处理一组数据,处理这些数据上的一个或多个视图,并操作这些数据.
子系统 | 实例数 | 功能 | 公开的接口 | 使用的接口 |
---|---|---|---|---|
GamePlay | 1 | 开始游戏 控制游戏进度 控制绘图 控制胜方 结束游戏 |
游戏结束 | 轮流(Play提供) 绘图(ChessBoardView提供) |
ChessPieceView | 32 | 移动自身 检测合法移动 |
移动 检测移动 |
获取棋子(ChessBoard提供) 移动棋子(ChessBoard提供) |
... | ... | ... | ... | ... |
-
选择线程模型
-
指定每个子系统的类层次结构
- 指定每个子系统的类,数据结构,算法和模式
子系统 | 类 | 数据结构 | 算法 | 模式 |
---|---|---|---|---|
GamePlay | GamePlay类 | GamePlay对象包含 一个ChessBoard对象 和两个Player对象 |
让每个玩家轮流移动 | 无 |
ChessPiece | 抽象超类:ChessPiece 子类: Rook,Bishop,Knight King,Pawn,Queen |
每个棋子存储它在 棋盘上的位置 |
在棋盘的不同位置查询棋子, 判断棋子的移动是否合法 |
无 |
... | ... | ... | ... | ... |
开源库
开源运动的两个术语: 自由软件(free software)与开放源代码软件(open-source software)
开源库资源来源:
- https://www.boost.org
- https://www.gnu.org
- https://www.github.com/open-source
- https://www.sourceforge.net
设计合理接口
- 应用程序编程接口(API)
- 工具类或者库
- 子系统接口
- 组件接口
SOLID原则
- SRP(Single Responsiblity Principle):单一责任原则,设计组件时,应该关注单个任务或者一组任务,"高内聚".
- OCP(Open/Closed Principle):开放/关闭原则,设计的类应当具有扩展性,其行为应当是可拓展的.
- LSP(Liskov Substitution Priciple):里氏替换原则,对于一个对象而言,应当能使用该对象的子类型替代该对象的实例.
- ISP(Interface Segregation Principle):接口隔离原则,为了在提供足够功能的同时降低复杂性,可提供两个独立接口.
- DIP(Dependency Inversion Principle):依赖倒置原则,使用接口倒置依赖关系.