294 lines
13 KiB
Markdown
294 lines
13 KiB
Markdown
# ReadSDK 代码规范
|
||
|
||
## 适用范围
|
||
|
||
本文档适用于 `ReadSDK` 新增代码与重构代码。
|
||
|
||
- 规范覆盖 `Sources/` 与 `RDReaderDemo/` 中的 Swift 代码。
|
||
- 命名、分层与职责边界以 SDK 可维护性和可扩展性为优先。
|
||
- 本文档分为两类内容:
|
||
- `已观察到的约定`:当前工程常见写法。
|
||
- `建议统一的规范`:后续统一执行的规则。
|
||
|
||
## 新架构目录与职责
|
||
|
||
### 已观察到的约定
|
||
|
||
- `Sources/RDReaderView`:阅读核心容器、翻页能力与基础视图。
|
||
- `EPUBCore`:EPUB 解析、资源定位、导航状态、分页与会话协调。
|
||
- `EPUBTextRendering`:文本渲染引擎与分页支持。
|
||
- `EPUBUI`:可开箱即用的 Reader UI 层。
|
||
- `RDReaderDemo`:示例应用与调试入口。
|
||
|
||
### 建议统一的规范
|
||
|
||
- 新增业务能力优先归入 `Sources/RDReaderView` 下的对应模块目录。
|
||
- `Core` 层只承载解析、会话、状态机与通用能力,不写页面级交互。
|
||
- `UI` 层只承载展示、事件分发和轻量状态同步,不直接处理底层解析逻辑。
|
||
- 若模块持续膨胀,优先在当前模块下继续拆分子文件,不跨目录散落实现。
|
||
|
||
## 命名规范
|
||
|
||
### 已观察到的约定
|
||
|
||
- 当前工程历史命名以 `SS`、`RDEPUB` 开头。
|
||
- 控制器常用 `...Controller`,视图常用 `...View`,会话对象常用 `...Session`。
|
||
- 扩展文件采用 `类型名+功能域.swift`,如 `Parser+Archive.swift`。
|
||
- 事件方法常使用 `Action` 结尾,例如 `pageTapAction()`、`themeChangeAction()`。
|
||
- 绑定数据的方法常使用 `bind`、`update`、`refresh`、`configure` 等动词。
|
||
|
||
### 建议统一的规范
|
||
|
||
- **所有新增类型必须以 `RD` 开头。**
|
||
- EPUB 相关类型统一以 `RDEPUB` 开头。
|
||
- 方法名、变量名沿用 Swift 小驼峰,不增加额外前缀。
|
||
- 类型名应反映职责,不使用过宽泛的后缀;只有真正承担协调逻辑时才使用 `Manager`、`Handler`。
|
||
- 事件处理方法统一使用"对象/意图 + Action"命名,例如 `pageTapAction`、`themeChangeAction`。
|
||
- 数据绑定方法优先使用以下语义:
|
||
- `bind...`:将模型绑定到视图或模块。
|
||
- `update...`:增量刷新已有界面或状态。
|
||
- `configure...`:一次性配置样式或依赖。
|
||
- `refresh...`:重新拉取或重建数据状态。
|
||
- 避免新增拼写不一致的方法名;若发现历史命名拼写错误,新增代码必须使用正确拼写,旧接口修复时应配合调用点一起调整。
|
||
|
||
命名示例:
|
||
|
||
- `RDReaderView`
|
||
- `RDEPUBParser`
|
||
- `RDEPUBPublication`
|
||
- `RDEPUBReadingSession`
|
||
- `RDEPUBReaderController`
|
||
- `RDEPUBReaderTheme`
|
||
|
||
扩展文件命名示例:
|
||
|
||
```text
|
||
RDEPUBParser.swift
|
||
RDEPUBParser+Archive.swift
|
||
RDEPUBParser+Package.swift
|
||
RDEPUBParser+TOC.swift
|
||
RDEPUBParser+Resources.swift
|
||
RDEPUBWebView.swift
|
||
RDEPUBWebView+Configuration.swift
|
||
RDEPUBWebView+Reflowable.swift
|
||
```
|
||
|
||
## 分层与职责边界
|
||
|
||
### 已观察到的约定
|
||
|
||
- 阅读入口负责容器装配、翻页模式切换和事件分发。
|
||
- EPUB 核心层负责解析、导航、分页、资源读取与定位。
|
||
- 渲染层负责 HTML/富文本渲染与分页支持。
|
||
- UI 层负责主题、工具栏、目录、设置等交互能力。
|
||
|
||
### 建议统一的规范
|
||
|
||
- `RD...Controller`:负责页面级编排与流程调度,不承载复杂渲染细节。
|
||
- `RD...View`:负责展示与局部交互,不承载完整业务流程。
|
||
- `RDEPUB...Core`:负责解析、会话状态与数据模型,不依赖具体页面。
|
||
- 配置、主题、定位、进度模型统一下沉为 `struct`。
|
||
- 跨层通信优先通过会话层或协议,不做跨层直接写状态。
|
||
|
||
## UI 与布局规范
|
||
|
||
### 已观察到的约定
|
||
|
||
- 视图多采用 `lazy var` 初始化,并在闭包内完成默认配置。
|
||
- 自定义 View 通常在 `init(frame:)` 或业务绑定后调用 `initView()` 完成视图树搭建。
|
||
- 复杂页面使用分区 extension 组织代理与事件实现。
|
||
- 页面或组件内部按职责拆分样式方法,例如 `topBarStyle()`、`contentStyle()`。
|
||
- 页面经常通过回调闭包把交互抛给外层,例如 `pageChangeCallback`、`selectionCallback`。
|
||
- 模块内存在多处布局方式混用情况。
|
||
|
||
### 建议统一的规范
|
||
|
||
- SDK 层新增 UI 使用 Auto Layout 原生约束,Demo 层可使用 SnapKit;单文件内不混用多套布局体系。
|
||
- 视图层初始化顺序保持一致:
|
||
- 定义属性与子视图
|
||
- 在 `initView()` 中组装视图树
|
||
- 在独立方法中拆分样式和状态刷新逻辑
|
||
- 当约束会被多次切换时:
|
||
- 首次创建使用 `makeConstraints`(SnapKit)或 `NSLayoutConstraint`
|
||
- 重建结构使用 `remakeConstraints`(SnapKit)或先移除再添加
|
||
- 仅修改常量时使用 `updateConstraints`(SnapKit)或修改 `constant` 属性
|
||
- 布局分支明显时,优先拆成语义化私有方法,不要把所有状态分支堆在一个超长方法里。
|
||
- 对外暴露的 UI 刷新入口建议以 `bind` 或 `update` 开头,避免把布局细节暴露给调用方。
|
||
- 交互事件通过闭包或 delegate 抛出,避免子视图持有上层业务依赖。
|
||
|
||
## 交互与状态处理规范
|
||
|
||
### 已观察到的约定
|
||
|
||
- 事件处理使用 `@objc` + selector。
|
||
- 异步回调中广泛使用 `[weak self]`。
|
||
- 状态判断常通过 `guard` 提前返回。
|
||
|
||
### 建议统一的规范
|
||
|
||
- 按钮、通知、系统回调放入独立 extension 分组。
|
||
- 异步闭包默认先使用 `[weak self]`,仅在必要时改强引用。
|
||
- 多前置条件入口统一先 `guard` 校验,减少嵌套。
|
||
- 导航与进度恢复统一通过 `RDEPUBReadingSession` 协调。
|
||
|
||
## 代码风格细则
|
||
|
||
### 已观察到的约定
|
||
|
||
- extension 分区较常见。
|
||
- 解析与渲染模型多数采用 `struct`。
|
||
- 存在少量历史命名不统一与可选值处理不一致情况。
|
||
|
||
### 建议统一的规范
|
||
|
||
- 默认遵循”最小可见性”:`private` > `fileprivate` > `internal` > `public`。
|
||
- 纯数据模型使用 `struct` + `Codable` + `Equatable`。
|
||
- 服务对象使用 `final class`,避免无意义继承。
|
||
- 错误类型统一使用 `enum + LocalizedError`。
|
||
- 新增代码避免强制解包;若当前上下文无法避免,至少先在上层收敛边界。
|
||
- 统一优先使用 `guard` 做前置失败处理,减少深层嵌套。
|
||
- extension 的拆分原则以”单一职责”优先:
|
||
- 事件处理一组
|
||
- 代理 / DataSource 实现一组
|
||
- 工具方法一组
|
||
- 通知适配一组
|
||
- 调试代码继续使用 `#if DEBUG` 包裹,不把调试边框、日志、测试分支直接带入正式逻辑。
|
||
|
||
## 文件组织规范
|
||
|
||
### 已观察到的约定
|
||
|
||
- 目录按阅读容器、EPUB Core、渲染、UI 分层组织。
|
||
- 大类通过 `+Extension` 文件拆分职责。
|
||
|
||
### 建议统一的规范
|
||
|
||
- 目录保持以下分层,不跨层放置实现:
|
||
- `Sources/RDReaderView/EPUBCore`
|
||
- `Sources/RDReaderView/EPUBTextRendering`
|
||
- `Sources/RDReaderView/EPUBUI`
|
||
- 单文件建议不超过 `600` 行;超出后按职责拆分 extension 文件。
|
||
- extension 文件命名统一 `RD类型名+功能域.swift`。
|
||
|
||
## 注释规范
|
||
|
||
- 注释、错误提示、日志统一使用中文。
|
||
- 关键流程方法保留”为什么这样做”的注释,不写重复代码字面行为的注释。
|
||
- 调试输出统一放在 `#if DEBUG` 下。
|
||
- 新代码保留有信息量的注释,避免重复描述显而易见的代码行为。
|
||
|
||
## 已观察到的项目模式
|
||
|
||
### 模式 1:统一入口 + 扩展拆分
|
||
|
||
- `RDEPUBParser` 负责 EPUB 解析主入口,不同能力拆到 `+Archive`、`+Package`、`+TOC`、`+Resources` 等扩展文件。
|
||
- `RDEPUBReadingSession` 负责阅读会话主入口,状态管理、分页、定位等能力拆到扩展文件。
|
||
- 该模式适合继续用于解析器、会话管理、控制器工具方法等横向能力。
|
||
|
||
### 模式 2:页面编排在 Controller,局部交互下沉到 View
|
||
|
||
- `RDEPUBReaderController` 负责阅读页整体编排:翻页容器装配、工具栏切换、阅读位置恢复。
|
||
- `RDReaderView` 负责分页容器布局与翻页交互,并通过 DataSource / Delegate 把数据需求交回外层。
|
||
- 该模式保持 Controller 管流程、View 管展示的职责分离。
|
||
|
||
### 模式 3:列表与容器逻辑通过扩展拆开
|
||
|
||
- 复杂容器将 `UICollectionViewDataSource`、`UICollectionViewDelegate`、`UICollectionViewDelegateFlowLayout` 分别拆分到 extension。
|
||
- 该模式降低单文件中主逻辑与代理逻辑的耦合,适合继续用于任何包含列表或容器的组件。
|
||
|
||
### 模式 4:渲染路径抽象
|
||
|
||
- `RDEPUBReadingProfile` 根据 EPUB 特征自动选择渲染路径(`webFixedLayout` / `webInteractive` / `textReflowable`)。
|
||
- 新增渲染相关功能时,必须评估对三种路径的覆盖情况。
|
||
|
||
## 待统一项
|
||
|
||
- 当前访问控制级别存在混用:同一类里 `public`、默认 `internal`、`private` 并存,建议后续新增代码默认从最小可见范围开始声明。
|
||
- 当前存在少量强制解包,建议新增代码优先通过前置校验收敛风险。
|
||
- 当前存在拼写不一致问题,建议后续新增代码统一使用标准英文单词,旧接口如需修复应配合调用点一起调整。
|
||
- 当前部分注释偏”过程说明”或遗留调试注释,建议新代码保留有信息量的注释。
|
||
- 当前个别 View 在数据绑定阶段再次调用 `initView()` 重建界面,这种方式在复杂组件中容易引入重复添加子视图或状态不一致。建议新增组件优先区分”初始化视图结构”和”刷新数据状态”两个阶段。
|
||
|
||
## 禁忌事项
|
||
|
||
| 禁忌 | 替代做法 |
|
||
|------|----------|
|
||
| 新增类型不加 `RD` 前缀 | 所有新增类型统一 `RD` / `RDEPUB` 前缀 |
|
||
| UI 层直接拼装解析状态 | 通过 `RDEPUBReadingSession` 获取状态 |
|
||
| 控制器直接操作底层解析细节 | 通过 `RDEPUBPublication`、`RDEPUBParser` 暴露接口 |
|
||
| 强制解包可选值 | `guard let` / `if let` |
|
||
| 用页号单独恢复阅读进度 | 统一使用 `href + progression` |
|
||
|
||
## 使用建议
|
||
|
||
- 新增功能前先确定目录归属和职责边界。
|
||
- 命名先定前缀再落代码:类型一律 `RD` 开头。
|
||
- 若需迁移历史 `SS` 前缀,按模块渐进替换,优先替换新增与重构触达文件。
|
||
|
||
## 旧 SS 命名迁移到 RD 的分阶段执行清单
|
||
|
||
### 阶段 0:冻结新增 SS 命名(立即执行)
|
||
|
||
- 目标:从当前时点开始,不再引入新的 `SS`/`RDEPUB` 类型名。
|
||
- 动作:
|
||
- 新增类型统一使用 `RD`/`RDEPUB` 前缀。
|
||
- Code Review 增加命名检查项:发现新增 `SS` 命名必须驳回。
|
||
- 在 PR 模板中加入“本次是否新增旧前缀命名”勾选项。
|
||
- 验收:
|
||
- 新提交代码中,新增类型 `SS` 前缀数量为 `0`。
|
||
|
||
### 阶段 1:建立迁移映射表(第 1 周)
|
||
|
||
- 目标:明确“旧名 -> 新名”一一映射,避免多人并行改名冲突。
|
||
- 动作:
|
||
- 统计核心公开类型、内部核心类型、测试类型三类清单。
|
||
- 建立命名映射表,例如:
|
||
- `RDReaderView` -> `RDReaderView`
|
||
- `RDEPUBParser` -> `RDEPUBParser`
|
||
- `RDEPUBReadingSession` -> `RDEPUBReadingSession`
|
||
- 对外 API 单独标记“需兼容过渡”的类型。
|
||
- 验收:
|
||
- 映射表覆盖全部高频核心类型,且团队评审通过。
|
||
|
||
### 阶段 2:先迁移内部类型(第 2-3 周)
|
||
|
||
- 目标:优先改内部实现,降低外部兼容压力。
|
||
- 动作:
|
||
- 按模块分批迁移:`EPUBCore` -> `EPUBTextRendering` -> `EPUBUI`。
|
||
- 每批次只改一个子模块,避免超大 PR。
|
||
- 同步修复调用点、扩展文件名与注释中的旧命名。
|
||
- 验收:
|
||
- 目标模块内类型命名全部满足 `RD` 规则。
|
||
- 编译通过,Demo 阅读主流程可用。
|
||
|
||
### 阶段 3:迁移公开 API 并保留兼容层(第 3-4 周)
|
||
|
||
- 目标:完成对外接口改名,同时给接入方提供平滑升级窗口。
|
||
- 动作:
|
||
- 对外公开类型切换为 `RD` 命名。
|
||
- 旧公开类型保留兼容别名,并标注废弃说明(`deprecated`)。
|
||
- 在 Release Note 提供“旧名/新名对照表”和迁移示例。
|
||
- 验收:
|
||
- 新接入示例仅使用 `RD` 命名。
|
||
- 旧接入代码在兼容期内无需立即改动即可编译。
|
||
|
||
### 阶段 4:清理兼容层与收口(下一主版本)
|
||
|
||
- 目标:在约定主版本移除旧前缀,完成命名收口。
|
||
- 动作:
|
||
- 删除 `SS` 兼容别名与过渡代码。
|
||
- 清理文档、注释、示例工程中的旧前缀残留。
|
||
- 对外发布最终迁移公告与升级说明。
|
||
- 验收:
|
||
- 工程内无 `SS`/`RDEPUB` 类型定义残留。
|
||
- 文档与示例代码全部为 `RD`/`RDEPUB` 命名。
|
||
|
||
### 迁移过程约束
|
||
|
||
- 每次迁移 PR 必须包含:
|
||
- 命名改动清单
|
||
- 影响范围说明
|
||
- 回归验证结果(编译、Demo 主流程、关键阅读路径)
|
||
- 禁止在同一 PR 中同时做“大规模命名迁移 + 业务逻辑重构”。
|
||
- 若改名会影响外部接入,必须先补迁移文档再合并代码。
|