ReadViewSDK/Doc/CODING_STYLE.md
2026-05-21 19:40:51 +08:00

13 KiB
Raw Blame History

ReadSDK 代码规范

适用范围

本文档适用于 ReadSDK 新增代码与重构代码。

  • 规范覆盖 Sources/RDReaderDemo/ 中的 Swift 代码。
  • 命名、分层与职责边界以 SDK 可维护性和可扩展性为优先。
  • 本文档分为两类内容:
    • 已观察到的约定:当前工程常见写法。
    • 建议统一的规范:后续统一执行的规则。

新架构目录与职责

已观察到的约定

  • Sources/RDReaderView:阅读核心容器、翻页能力与基础视图。
  • EPUBCoreEPUB 解析、资源定位、导航状态、分页与会话协调。
  • EPUBTextRendering:文本渲染引擎与分页支持。
  • EPUBUI:可开箱即用的 Reader UI 层。
  • RDReaderDemo:示例应用与调试入口。

建议统一的规范

  • 新增业务能力优先归入 Sources/RDReaderView 下的对应模块目录。
  • Core 层只承载解析、会话、状态机与通用能力,不写页面级交互。
  • UI 层只承载展示、事件分发和轻量状态同步,不直接处理底层解析逻辑。
  • 若模块持续膨胀,优先在当前模块下继续拆分子文件,不跨目录散落实现。

命名规范

已观察到的约定

  • 当前工程历史命名以 SSRDEPUB 开头。
  • 控制器常用 ...Controller,视图常用 ...View,会话对象常用 ...Session
  • 扩展文件采用 类型名+功能域.swift,如 Parser+Archive.swift
  • 事件方法常使用 Action 结尾,例如 pageTapAction()themeChangeAction()
  • 绑定数据的方法常使用 bindupdaterefreshconfigure 等动词。

建议统一的规范

  • 所有新增类型必须以 RD 开头。
  • EPUB 相关类型统一以 RDEPUB 开头。
  • 方法名、变量名沿用 Swift 小驼峰,不增加额外前缀。
  • 类型名应反映职责,不使用过宽泛的后缀;只有真正承担协调逻辑时才使用 ManagerHandler
  • 事件处理方法统一使用"对象/意图 + Action"命名,例如 pageTapActionthemeChangeAction
  • 数据绑定方法优先使用以下语义:
    • bind...:将模型绑定到视图或模块。
    • update...:增量刷新已有界面或状态。
    • configure...:一次性配置样式或依赖。
    • refresh...:重新拉取或重建数据状态。
  • 避免新增拼写不一致的方法名;若发现历史命名拼写错误,新增代码必须使用正确拼写,旧接口修复时应配合调用点一起调整。

命名示例:

  • RDReaderView
  • RDEPUBParser
  • RDEPUBPublication
  • RDEPUBReadingSession
  • RDEPUBReaderController
  • RDEPUBReaderTheme

扩展文件命名示例:

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()
  • 页面经常通过回调闭包把交互抛给外层,例如 pageChangeCallbackselectionCallback
  • 模块内存在多处布局方式混用情况。

建议统一的规范

  • SDK 层新增 UI 使用 Auto Layout 原生约束Demo 层可使用 SnapKit单文件内不混用多套布局体系。
  • 视图层初始化顺序保持一致:
    • 定义属性与子视图
    • initView() 中组装视图树
    • 在独立方法中拆分样式和状态刷新逻辑
  • 当约束会被多次切换时:
    • 首次创建使用 makeConstraintsSnapKitNSLayoutConstraint
    • 重建结构使用 remakeConstraintsSnapKit或先移除再添加
    • 仅修改常量时使用 updateConstraintsSnapKit或修改 constant 属性
  • 布局分支明显时,优先拆成语义化私有方法,不要把所有状态分支堆在一个超长方法里。
  • 对外暴露的 UI 刷新入口建议以 bindupdate 开头,避免把布局细节暴露给调用方。
  • 交互事件通过闭包或 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列表与容器逻辑通过扩展拆开

  • 复杂容器将 UICollectionViewDataSourceUICollectionViewDelegateUICollectionViewDelegateFlowLayout 分别拆分到 extension。
  • 该模式降低单文件中主逻辑与代理逻辑的耦合,适合继续用于任何包含列表或容器的组件。

模式 4渲染路径抽象

  • RDEPUBReadingProfile 根据 EPUB 特征自动选择渲染路径(webFixedLayout / webInteractive / textReflowable)。
  • 新增渲染相关功能时,必须评估对三种路径的覆盖情况。

待统一项

  • 当前访问控制级别存在混用:同一类里 public、默认 internalprivate 并存,建议后续新增代码默认从最小可见范围开始声明。
  • 当前存在少量强制解包,建议新增代码优先通过前置校验收敛风险。
  • 当前存在拼写不一致问题,建议后续新增代码统一使用标准英文单词,旧接口如需修复应配合调用点一起调整。
  • 当前部分注释偏”过程说明”或遗留调试注释,建议新代码保留有信息量的注释。
  • 当前个别 View 在数据绑定阶段再次调用 initView() 重建界面,这种方式在复杂组件中容易引入重复添加子视图或状态不一致。建议新增组件优先区分”初始化视图结构”和”刷新数据状态”两个阶段。

禁忌事项

禁忌 替代做法
新增类型不加 RD 前缀 所有新增类型统一 RD / RDEPUB 前缀
UI 层直接拼装解析状态 通过 RDEPUBReadingSession 获取状态
控制器直接操作底层解析细节 通过 RDEPUBPublicationRDEPUBParser 暴露接口
强制解包可选值 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 中同时做“大规模命名迁移 + 业务逻辑重构”。
  • 若改名会影响外部接入,必须先补迁移文档再合并代码。