13 KiB
13 KiB
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...:重新拉取或重建数据状态。
- 避免新增拼写不一致的方法名;若发现历史命名拼写错误,新增代码必须使用正确拼写,旧接口修复时应配合调用点一起调整。
命名示例:
RDReaderViewRDEPUBParserRDEPUBPublicationRDEPUBReadingSessionRDEPUBReaderControllerRDEPUBReaderTheme
扩展文件命名示例:
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/EPUBCoreSources/RDReaderView/EPUBTextRenderingSources/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->RDReaderViewRDEPUBParser->RDEPUBParserRDEPUBReadingSession->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 中同时做“大规模命名迁移 + 业务逻辑重构”。
- 若改名会影响外部接入,必须先补迁移文档再合并代码。