面向对象设计与构造 —— 第二单元总结

面向对象设计与构造 —— 第二单元总结
一、前言
第一次作业:实现基本逻辑门(AND、OR、NOT、XOR、XNOR),采用循环扫描模拟,通过引脚传播信号直至电路稳定,知识点以基本类设计和简单仿真流程为主,题量适中、难度入门;

第二次作业:引入三态门、译码器、多路选择器与分配器,继续沿用循环扫描架构,要求处理控制引脚与数据引脚的混合连接及高阻态(-1),题量和元件复杂度明显提升;

第三次作业:改为递归求值架构,支持嵌套子电路,并引入策略模式分离门逻辑,通过 App / Reader / Util / Checker / Runner 实现高度模块化,题量看似缩减实则设计门槛最高。

整体呈“基础仿真→功能扩展→架构重构与模式应用”的递进,梯度设计合理。

二、设计与分析
1.第一次作业 —— 基本门与循环扫描模拟
作业要求
设计一个支持基本逻辑门(AND、OR、NOT、XOR、XNOR)的数字电路模拟器。输入为文本形式的网表,程序需解析连接,通过循环扫描驱动引脚传播信号,直至电路稳定后输出所有门的计算结果。
实现方式
核心类 Circuit 持有输入信号列表(List)和门列表(List),提供 simulate() 方法执行 do-while 循环扫描。Gate 为抽象类,具体门类实现 evaluate() 方法。Pin 增加 value 属性和 drivenPins 列表,setValue() 时自动向下游传播。InputParser 负责解析并动态创建门实例。
image
image

类职责与 SourceMonitor 指标

类名 职责 Lines Statements %Branches Max Complexity Avg Complexity
Main 程序入口,读取所有行直到"end",创建 Circuit,调用 InputParser 解析,执行 simulate() 并输出有效门的值 30 16 18.8 4 4
Circuit 存储输入信号列表和门列表,提供 simulate() 循环扫描方法及门查找与创建功能 107 69 40.6 3 3
Gate (抽象) 门的抽象基类,持有名称、类型、引脚列表,定义 evaluate() 接口 40 29 10.3 3 3
AndGate 逻辑与运算 16 10 20.0 3 3
OrGate 逻辑或运算 16 10 20.0 3 3
NotGate 逻辑非运算 10 7 0.0 2 2
XorGate 异或运算 11 8 0.0 1 1
XnorGate 同或运算 11 8 0.0 2 2
Pin 简单数据类,存储引脚全名 29 25 4.0 2 2
InputParser 解析输入文本,构建主电路及所有子电路的连接关系、端口定义、输入信号值 107 76 27.6 15 15

心得
亮点:所有门类圈复杂度 ≤3,核心逻辑简单清晰;Circuit 分支覆盖率 40.6%,说明连接处理路径较全面。
待改进:InputParser 的 Max Complexity 高达 15,是典型的“上帝类”,解析、创建门、注册连接等职责过于集中;注释率普遍偏低(多数文件为 0%),可读性不足;循环扫描无收敛上限,若输入意外包含组合反馈环可能导致死循环。

2.第二次作业 —— 扩展元件与循环扫描模拟
作业要求
在基本门基础上新增三态门(TriStateGate)、译码器(Decoder)、多路选择器(Multiplexer)和多路分配器(Demultiplexer)。要求支持控制引脚与数据引脚的混合连接,且需正确处理高阻态(输出 -1)。模拟方式改为循环扫描,通过引脚主动推送信号直至电路稳定。
实现方式
架构彻底重构:Component 成为抽象父类,所有元件继承它并实现 evaluate()。Pin 增加 value 属性和 drivenPins 列表,setValue() 时自动向下游传播。Circuit 持有 inputSignals、components 列表,提供 simulate() 方法执行 do-while 循环,每轮扫描所有元件,若任一输出变化则继续。InputParser 通过工厂方法 createComponent() 动态创建各类门与功能模块。
image
image

类职责与 SourceMonitor 指标

类名 职责 Lines Statements %Branches Max Complexity Avg Complexity
Main 读取输入,调用 InputParser,执行 simulate() 并输出有效元件值 32 19 21.1 5 5
Circuit 管理输入信号、元件列表,提供 simulate() 循环扫描 123 73 34.2 1 1
Component (抽象) 所有元件的基类,定义 evaluate()、getType()、有效性检查 39 34 11.8 5 5
Gate (抽象) 继承 Component,基本门的基类 28 18 5.6 2 2
AndGate 逻辑与 16 13 23.1 4 4
OrGate 逻辑或 16 13 23.1 4 4
NotGate 逻辑非 11 10 10.0 3 3
XorGate 异或 12 11 9.1 2 2
XnorGate 同或 12 11 9.1 3 3
TriStateGate 三态门:使能端为1时输出数据,否则高阻态(-1) 28 21 4.8 3 3
Decoder 译码器:3控制端+N地址端,译码输出0,其余输出1 73 42 19.0 4 4
Multiplexer 多路选择器:根据控制端编码选择对应数据输入 61 43 18.6 5 5
Demultiplexer 多路分配器:将数据分配到由控制端选择的输出端 51 39 17.9 6 6
InputParser 解析 INPUT 行与连接行,动态创建 Component 实例并注册 133 104 32.7 7 7
Pin 增强版引脚,存储值、所属元件,维护 drivenPins 并自动传播 28 27 3.7 2 2

心得
亮点:引脚传播模型将模拟流程简化为“注入输入 → 循环扫描”,无需手动递归;Component 抽象体系使新增元件容易;Circuit.simulate() 复杂度仅 1,循环逻辑简洁。
待改进:InputParser 仍是复杂度重灾区(Max 7, Statements 104),承担过多职责;Multiplexer 重写了 isValid() 破坏继承契约;循环扫描无收敛上限,组合环可能导致死循环;主函数语句数 19,仍可精简。
小结:成功扩展元件库并实现动态仿真,但解析器膨胀和部分设计缺陷说明架构仍需优化。

3.第三次作业 —— 策略模式重构与高度模块化
作业要求
基于第一次作业的递归求值架构进行设计优化与重构。功能上仍需支持基本门与嵌套子电路,但要求通过策略模式分离门逻辑,利用工具类统一解析与校验,提升代码的模块化、可读性和可扩展性。
实现方式
重构后新增 App 作为调度中心,Reader 负责输入读取,Data 汇总解析结果。GateLogic 抽象类及具体策略(AndGateLogic 等)实现门运算,Gate 变为数据容器持有 GateLogic 引用。Util 类集中管理正则匹配、令牌分类等纯工具方法。Parser 和 Checker 职责纯粹,Runner 专注递归求值与输出收集。主函数极致精简至 5 行。
image
image

类职责与 SourceMonitor 指标

类名 职责 Lines Statements %Branches Max Complexity Avg Complexity
Main 调用 Reader 读取,创建 App 并运行 21 19 0.0 1 1
Reader 封装 BufferedReader,一次性读取所有输入行 17 13 7.7 2 1.5
App 调度中心:调用 Parser、Checker、Runner 31 19 26.3 6 6
Parser 解析主电路与子电路,支持中英文冒号 115 79 29.1 14 6
Checker 电路验证,检查信号源冲突并推断引脚数 81 58 31.0 11 6
Runner 递归求值器,带缓存与环检测 120 81 23.5 5 2.75
Util 静态工具类:令牌分割、端点分类、引脚提取等 109 82 28.0 9 3.56
Circuit 数据类:存储端口、连接、门、子电路引用 17 15 0.0 1 1
Gate 门描述符,持 name、logic、id、输入引脚数 20 16 0.0 1 1
GateLogic (抽象) 策略基类,定义 order 和 evaluate() 11 8 0.0 1 1
AndGateLogic 与逻辑策略 15 10 20.0 3 2
OrGateLogic 或逻辑策略 15 10 20.0 3 2
NotGateLogic 非逻辑策略 14 10 0.0 1 1
XorGateLogic 异或策略 14 10 0.0 2 1.33
XnorGateLogic 同或策略 14 10 0.0 2 1.33
Data / Connection / CountInfo / SubPort 辅助数据类 ≤11 ≤9 0.0 0~1 0~1

心得
亮点:
模块化程度极高:Main 仅 3 条逻辑语句,App 也只有 19 条,完美符合“入口方法不超过 10 行”的改进方向。
策略模式使 GateLogic 体系复杂度极低(1~3),扩展新门只需添加新策略类,符合开闭原则。
Runner 最大复杂度仅 5,通过 gateVisiting 集合有效防止环依赖,平均方法语句数 10.67,粒度合理。
Util 消除了重复解析逻辑,提升了代码复用度。
待改进:
注释率为 0%:报表显示所有文件注释率均为 0.0,这是致命缺陷。即使方法简短,也需至少对公共类和方法添加 Javadoc。
Parser 与 Checker 的复杂度仍然偏高(14、11),主要源于长条件分支,可进一步拆分为更小的处理函数。
辅助数据类完全无注释,其业务含义需靠猜测。
Gate.inferredInputCount 的动态推断机制缺乏文档说明,可能使维护者困惑。
小结:本次重构架构最优、扩展性最强,但文档化的缺失拖累了整体工程水平,证明代码质量 = 设计 + 可读性文档。

三、本单元踩过的坑与改进方向
坑1:解析器成为“上帝类”
第一次和第二次作业中,InputParser 的语句数和复杂度持续偏高(第一次 Max 15,第二次 Max 7,Statements 104),承担了读取、解析、创建组件、注册连接等多重职责。
教训:解析应按照“读取 → 词法分析 → 语法分析 → 对象构建”的管道模式拆分,避免单个类同时处理多级抽象。
坑2:循环扫描缺乏收敛保护
第二次作业的 do-while(changed) 循环没有最大迭代次数限制,若输入意外包含组合反馈环(如两个非门首尾相连),会导致死循环超时。
改进:增加 maxIterations = 1000 阈值,超过时抛出 CircuitOscillationException。
坑3:继承契约被破坏
Component.isValid() 默认要求所有输入引脚有效,但 Multiplexer 仅需被选中的数据引脚有效,被迫重写该方法,导致多态行为不一致。
反思:基类应只提供最小接口,将“必要引脚集”的定义下放给子类,或采用组合优于继承。
坑4:遗留代码与重复造轮子
第三次重构时,第一次作业的 Gate 和第二次作业的 Component 继承体系存在大量重复逻辑,未能抽取公共抽象层,导致代码冗余。
教训:每次迭代结束必须执行“代码清理”步骤,利用 IDE 查找未使用类和方法并删除;提取公共逻辑到共享库。
坑5:测试不足导致回归风险
第三次作业从循环扫描架构迁移到递归求值架构时,部分嵌套子电路的边界用例出现错误,但未被及时捕获。
建议:建立标准网表测试集,每次架构变更后自动回归所有用例。
坑6:注释率长期为零
三次作业中,大量实体类和核心方法没有 Javadoc,导致 SourceMonitor 注释率统计长期为 0%。
对策:在编码规范中加入“每个 public 类和方法必须有 Javadoc”的硬性要求,并纳入提交前检查。

四、改进建议
推行“解析器管道模式”
将 Parser 拆分为 Lexer(分词)、GrammarParser(语法树构建)、CircuitBuilder(语义构建)三个独立阶段,每阶段可单独测试和替换。
引入工厂模式统一组件创建
当前门创建逻辑散落在 Parser 或 Util 的 if-else 中,可抽象出 GateFactory 接口,每种门对应一个工厂,彻底解耦解析与构造。
完善异常处理体系
定义 CircuitException 基类及子类异常(如 SignalConflictException、UndefinedGateException),由 Checker 和 Runner 抛出,App 统一捕获并输出标准错误信息,杜绝 System.exit(0)。
强制文档化
在 .java 文件模板中添加类注释和公共方法注释骨架,并配置 Checkstyle 检查注释率,确保每次提交时注释率不低于 30%。
建立回归测试套件
收集过往所有通过的测试用例,编写自动化测试脚本,在重构或新增功能后一键运行,确保功能不回退。

五、总结
学到了什么
设计模式的价值:第三次作业通过策略模式彻底解耦门逻辑与数据,体会到了开闭原则和代码复用的巨大优势。
模块化的力量:将调度(App)、解析(Parser)、验证(Checker)、求值(Runner)分离后,每个模块都可独立理解、测试和演进。
重构的勇气:从第一次到第三次,代码经历了“能用 → 能用且正确 → 正确且优雅”的质变,认识到持续重构是保持代码健康的必需品。
数据驱动改进:SourceMonitor 的复杂度指标为精准优化提供了方向,能快速定位“坏味道”代码。
需要进一步研究的内容
时序电路模拟:如何引入时钟、状态锁存器,以及事件驱动仿真替代循环扫描。
更高级的设计模式:如访问者模式用于电路分析,观察者模式用于波形记录。
性能优化:拓扑排序减少重复计算、增量求值等。
自动化测试框架:学习 JUnit 5 和 Mockito,将本单元模拟器与标准测试框架集成。
对课程组织的建议
作业迭代之间可提供更清晰的接口定义,让学生专注扩展而非重复解析基础语法。
提供公开的标准测试用例集,减少因理解偏差带来的无效调试。
鼓励代码互评,从设计和实现两个维度互相学习,而不仅仅关注通过测评。
下单元学习计划
重点学习面向对象三大特性中的继承与多态的高级应用,尝试在表达式解析等场景中实践工厂模式、策略模式以及简单的解析器组合子。同时着手建立个人代码模板和测试习惯,逐步形成工业级编码素养。

标签
Java 面向对象 数字电路模拟 递归求值 循环扫描 策略模式 SourceMonitor UML类图 Mermaid 迭代开发