# cilly-vm-cpp 重构复盘(第 1 阶段:SRP)

1. 目标与范围

这次重构只做一件事:围绕 SRP(单一职责原则) ,把 VM 里的"执行职责"和"统计职责"拆开。

重构范围如下:

  1. 栈组件从"带统计"改为"纯数据结构"。
  2. VM 通过观察者发布事件,不再内置统计接口。
  3. 同步修复测试与构建系统(Bazel 目标、依赖、测试名)。

2. 改造前的问题

改造前,VMStackStats 的职责边界不清晰:

  1. StackStats 同时负责 栈存储统计计数
  2. VM 对外暴露了 PushCount/PopCount/Depth/MaxDepth 等统计接口。
  3. 统计逻辑和执行逻辑耦合,改一处会牵动核心路径。

问题本质是:功能可用,但长期维护和扩展成本会持续升高。


3. 本次改动清单

3.1 栈组件更名与瘦身

  1. 文件重命名:
    • src/stack_stats.h -> src/vm_stack.h
    • src/stack_stats.cc -> src/vm_stack.cc
    • tests/stack_stats_test.cc -> tests/vm_stack_test.cc
  2. 类型更名:
    • StackStats -> VmStack
  3. 功能调整:
    • 删除统计职责,仅保留 Push/Pop/Top/At/Clear/Empty 等栈行为。
    • 保留 values() 只读接口供 GC root 扫描使用。

3.2 观察者机制落地(栈统计)

新增 IVmObserver / VmStackObserver

  1. IVmObserver 提供:
    • OnStackPush()
    • OnStackPop()
    • OnTestEmit(const Value&)
  2. VmStackObserver 专门负责统计:
    • push_count_
    • pop_count_
    • max_depth_

3.3 test hook 迁移到观察者

  1. VM 增加并打通测试事件路径:
    • TestEmit(const Value&)
    • NotifyTestEmit(const Value&)
  2. __test_emit(x) builtin 仍保持脚本语义不变,但内部统一走 VM 事件广播。
  3. 新增 VmHookObserver(基于 OnTestEmit),用于测试侧采集 emit 值。
  4. 测试代码从旧接口迁移为 observer 方式:
    • 旧:vm.SetTestEmitSink(...)
    • 新:VmHookObserver hook; vm.AddObserver(&hook);

3.4 VM 迁移

  1. VM 内部栈类型切换为 VmStack
  2. VM 增加 AddObserver(IVmObserver*)
  3. VM::Push/Pop 中增加事件通知:
    • Push 后广播 OnStackPush
    • Pop 后广播 OnStackPop

3.5 Bazel 构建修复

本次重构中,构建系统发生了两类问题并已修复:

  1. 测试目标名未同步
    • stack_stats_test 已改为 vm_stack_test 并更新到 test_suite(all)
  2. frontend 子包依赖冲突
    • 移除 //src/frontend:frontend 残留依赖。
    • //src:cilly_core 统一收口源码。
    • 修复 src/BUILD.bazeltests/BUILD.bazel 的依赖关系。

4. 验证结果

执行命令:

bash 复制代码
bazelisk test //tests:"all" --test_output=errors

结果:38/38 测试全部通过

说明这次 SRP 重构没有破坏现有行为。


5. 改造收益

  1. 职责更清晰
    VmStack 只管数据,统计逻辑归 VmStackObserver,VM 只负责执行与事件广播。

  2. 扩展更简单

    后续加 profiler/debugger,只需新增 observer,不必反复改 VM 核心执行路径。

  3. 构建更稳定

    Bazel 依赖图更干净,减少跨包引用造成的隐式构建错误。


6. 阶段结论

第 1 阶段(SRP)目标达成:

"将统计职责从核心执行路径剥离"已经完成,且测试全绿。

下一阶段可以按计划推进:

  1. DIP:GC 接口抽象化。
  2. OCP:Generator/AST 访问者化重构。
相关推荐
「QT(C++)开发工程师」11 小时前
C++17三大实用特性详解:内联变量、std::optional、std::variant
jvm·c++
不爱吃炸鸡柳11 小时前
C++ STL 核心:string 从入门到精通(面试+源码+OJ实战)
java·c++·面试
南境十里·墨染春水11 小时前
C++笔记 Lambda表达式
开发语言·c++·笔记
悟渔11 小时前
用于STM32的C++编程的LED对象
c++·stm32·单片机
17(无规则自律)11 小时前
DFS:带重复项的全排列,程序运行全流程解析
c++·算法·深度优先
郝学胜-神的一滴12 小时前
「栈与缩点的艺术」二叉树前序序列化合法性判定:从脑筋急转弯到工程实现
java·开发语言·数据结构·c++·python·算法
AIminminHu12 小时前
OpenGL渲染与几何内核那点事-项目实践理论补充(三-1-(3):番外篇-当你的CAD打开“怪兽级”STL时:从内存爆炸到零拷贝的极致优化
c++·零拷贝·mmap·内存拷贝
水饺编程12 小时前
第4章,[标签 Win32] :SysMets3 程序讲解04,垂直滚屏重绘
c语言·c++·windows·visual studio
xiaoye-duck12 小时前
《算法题讲解指南:动态规划算法--子序列问题(附总结)》--32.最长的斐波那契子序列的长度,33.最长等差数列,34.等差数列划分II-子序列
c++·算法·动态规划
BestOrNothing_201512 小时前
C++零基础到工程实战(1.3):cpp注释与输出详解
c++·注释·命名空间·初学者教程·cout输出