用途 这个技能用于约束 Kotlin 项目中“模型映射层”的写法:传输载荷(transport payload)、领域模型(domain model)与持久化行(persistence row)之间的每一种转换,都集中放到专门的映射文件中,并且只写成纯扩展函数。以两跳映射为例:transport payload ──► domain model ──► persistence row。其中数据模块中的载荷到模型转换标为 internal,共享模块中的模型与持久化行之间的转换保持 public,两个方向分开声明。
主要能力与理由 资料主张使用扩展函数而非成员方法,因为映射的两端不应彼此知晓。若把 toSongEntity() 写在模型上,模型模块就要依赖持久化模块;若写在数据访问对象上,每次查询都会变成转换点。
资料列出的关键约束:
-
每个函数只做一个方向,且双向映射不保证往返一致(round-trip)。示例中下行映射只保留缩略图列表的最后一项,上行映射重建一个单元素列表并编造宽高,还通过 URL 字符串中的尺寸标记重新推导分类标志。资料建议不要写断言 x.toEntity().toModel() == x 的测试,因为它只对碰巧保留下来的字段通过,反而把猜测行为固定下来。
-
映射器会为源数据中原本没有的字段编造状态,这仅在“忽略冲突”的插入中安全。示例中下行映射把用户可见列(点赞标志、总播放时长、下载状态)填零;如果用于替换式插入或更新,每次刷新都会抹掉用户自己的数据。应在写路径上复用映射器前检查冲突策略,并优先使用显式的部分更新。
-
归一化(normalization)只应在它是输入的纯函数时放在映射层,例如改写图片 URL 中的尺寸标记、把 null 补为空串、把缺失计数钳制为零。需要时钟、设置、网络调用或数据库读取的,不属于归一化,应留在调用处。
-
不得从无关来源填充字段。资料举例说某字段曾因可空且空闲而被塞入显示副标题,导致下游各自解释该字段、某界面依赖了错误含义;修复办法是给第二个值单独建字段并设默认值,让原字段只表示一个含义。
-
为“含义”设默认值与为“值”设默认值(如 ?: "")是两件不同的事;对无法识别或历史数据的字符串含义判定,资料指向另一主题 enum-normalize-over-legacy-data。
-
传输载荷到模型的函数保持 internal,使“任何界面都不持有传输类型”成为编译期规则而非评审习惯;模型到持久化行的函数保持 public,因为仓储层和持久化层都需要。
使用方式(验证) 资料给出三条 grep 检查:
-
检查映射文件纯净性: MAPPER="path/to/YourMapping.kt" grep -nE "suspend fun|withContext|Dispatchers|runBlocking|^import (java.|kotlinx.coroutines|io.ktor|androidx.room)" "$MAPPER" || echo "(clean)" 应无输出。
-
列出声明的映射方向,按表核对每个重要配对应出现两次(各一个方向): grep -hoE "fun [A-Za-z0-9_.<>?]+.to[A-Za-z0-9_]+(" "$MAPPER"
| sed -E 's/^fun (.+).to([A-Za-z0-9_]+)($/\1 -> \2/' | sort -
在把下行映射用于写路径前,查看写操作对已有行的策略: grep -rhoE "OnConflictStrategy.(Companion.)?[A-Z]+" --include="*.kt" . | sed 's/Companion.//' | sort | uniq -c 每个替换式插入都是映射器编造默认值覆盖已存状态的地方,需逐项对照承载用户状态的表,确认写入值来自行本身而非映射器。
适用场景与限制 适用于用 Kotlin 编写、存在传输层—领域层—持久化层分层且需要组织映射代码的项目。资料强调映射文件纯度的意义:映射器一旦挂起、切换调度器或打日志,就不再能从顶到底通读。示例中被检查的两个映射文件之一只导入模型类型和一个注解,另一个只多了三个解析器和一个列表转换器,均为自变量的纯函数。资料未提供该技能的安装命令、代码库结构要求、框架版本或任何效果与性能数据。