Files
osmWorkflow/.trellis/spec/guides/code-reuse-thinking-guide.md
2026-08-04 09:45:51 +08:00

6.3 KiB
Raw Permalink Blame History

代码复用思考指南

目的:在新增 helper、常量、配置字段或枚举表之前先判断这个项目里"应该复用"和 "刻意重复"的边界。这里的关键不是追求抽象,而是避免事实漂移。


先搜索,再决定

改任何值或新增类似逻辑前先跑:

grep -rn "关键字或现有值" scripts blender config

本项目的重复有两类:

  • 危险重复:同一事实被多处维护,漏改会静默错产物
  • 可接受重复:运行时边界不同或独立入口需要保留,抽象会扩大耦合

判断之前不要凭直觉抽取。


必须复用的事实源

九个 osm2streets 图层

JS 侧只认 scripts/lib/scene-layers.js:15SCENE_LAYERS。 需要文件名、合并场景、style JSON、QGIS 颜色时,使用同文件导出的派生函数:

  • layerFile(layer)scene-layers.js:103
  • mergeScene(getCollection)scene-layers.js:109
  • sceneStyle()scene-layers.js:126
  • qgisRgba(hex, alpha)scene-layers.js:143

不要在 build-osm2streets-qgis.jsreimport-gpkg.js 或 QGIS 项目生成代码里再枚举 九个图层。旧问题正是同一顺序复制到四处,漏一处不报错,只让 Blender/Cesium 场景错栈。

Python 侧必须有 blender/osmassets/catalog.py:28ROAD_LAYERS,因为它还声明 Blender 高度与线性颜色。两侧靠 catalog.check_layers() 对账集合和顺序;颜色故意不同步。

区域输出路径

输出路径只在 scripts/lib/area-config.jsnormalizeAreaConfig() 推导。 scripts/build-area.jsscripts/diagnose-area.js 都必须通过 readAreaConfig() 读取 区域配置。低层脚本读取 _pipeline/osm2streets-qgis.config.json,不要重新读取 config/areas/*.json 或在阶段函数里现场拼路径。

新增产物时,在 area-config.jsoutputs 里加一项,再按需写入 writeDerivedConfig()。这样 intermediatesreimportblendercesiumpreview 和读-only 诊断仍然只通过磁盘产物耦合。

材质声明

Blender 内材质声明集中在 catalog.MATERIALScatalog.py:56)。 真实 bpy.types.Materialmaterials.from_spec()materials.py:198)构建。

Cesium 导出调色也属于同一个材质声明:新场景把 catalog.MATERIALS[*]["cesium"] 序列化到 material["cesium_export"]export_cesium.py 优先读这个属性。四张 export_cesium.py 材质名表仍存在,但只作为旧 .blend 的兼容回退。

改材质名时不能只改 catalog;必须全仓 grep 材质名,确认新契约和旧回退路径都合理。


可接受的重复

多份 parseArgs

parseArgs 现在重复在多个独立入口:

  • scripts/build-area.js:54
  • scripts/build-osm2streets-qgis.js:153
  • scripts/reimport-gpkg.js:93
  • scripts/compress-glb.js:16
  • scripts/diagnose-area.js:17

语义一致:--kebab-case valuekebabCase: "value",无值 flag 变字符串 "true"

这份重复目前是可接受技术债,因为这些脚本都能独立运行。改其中一处解析语义时,不要顺手 只改一份;要么保持全部入口一致,要么把"抽公共模块"作为独立重构并跑对应入口检查。

JS 与 Python 的图层颜色

scene-layers.js 的颜色是 QGIS 2D 调试 sRGB hexcatalog.py 的颜色是 Blender 线性 RGB。catalog.py:11-15 明确说颜色不是同步目标。

把两边颜色抽成同一个表不是复用,是破坏两个运行时各自调过的视觉结果。


重复模式检查

看到第二份枚举表

问:

  • 这份表是否已经能从 SCENE_LAYERSROAD_LAYERSMATERIALS 或配置派生?
  • 如果必须跨语言重复,是否已有对账机制?
  • 追加顺序是否影响 GLB 材质索引?

没有对账机制的重复表必须特别谨慎。材质名覆盖就是当前已知风险: generate_scene.py 创建材质,export_cesium.py 靠字符串覆盖,没有校验。

看到多个模块同样预处理

water.py:9grass.py:9scrub.py:8 都调用 clip_polygon,这是对要素模块签名的 统一要求:模块接收边界、自己裁剪、退化输入返回 0。

新增第四个要素模块时先照这个形状写,不要把裁剪逻辑上移到调用方。否则旧模块和新模块 的边界会不同,真实 OSM 的越界几何会按要素类型表现不一致。

看到多个地方解析同一格式

优先找已有解析器:

  • OSM XML → osmassets/osm.py:parse_osm()
  • 米制几何 → osmassets/geom.py
  • GeoJSON 场景合并 → scene-layers.js:mergeScene(getCollection)
  • 区域配置 → scripts/lib/area-config.js:normalizeAreaConfig()

如果确实需要新解析器,把输入格式、容错语义和调用者写清楚,并给纯 Python 逻辑补测试。


什么时候抽象

抽象只在满足至少一条时做:

  • 同一事实会被三处以上消费,且有真实漏改风险
  • 同一段校验逻辑跨多个入口影响产物安全
  • 抽出来后能保留运行时边界,比如纯 Python 逻辑进入 geom.py 后可被 python3 -m unittest discover blender/tests 覆盖

不要因为代码相似就抽象:

  • 多份 parseArgs 当前保持独立入口价值
  • ROAD_LAYERSSCENE_LAYERS 跨语言且承载不同字段
  • 每个要素模块各自调用 clip_polygon 是模块边界,不是可消除重复

提交前自检

  • 已 grep 关键值或新字段
  • 没有新增第二份九图层枚举
  • 没有在阶段函数里重新拼输出路径
  • 改材质名时已检查 catalog.pygenerate_scene.pyexport_cesium.py
  • 新纯几何逻辑放进 geom.py 并补 test_pure.py
  • 声称产物不变的重构已按产物一致性指南校验

反模式

反模式 后果
新增一份图层名列表 回到旧的四份同步,漏改静默错栈
把两套颜色表统一 破坏 QGIS 与 Blender 各自调过的视觉结果
低层脚本直接读 config/areas/*.json 两层配置边界失效
只改一份 parseArgs 的语义 独立入口行为分裂
把要素模块裁剪逻辑挪到调用方 不同要素的越界处理开始漂移
只改 export_cesium.py 的旧回退表,不写 MATERIALS[*]["cesium"] .blend 不会携带 Cesium 导出契约