Java 课程数字电路单元结束了,本单元以数字电路仿真程序为中心,分三次迭代持续扩充元件、子电路、异常校验功能,不断增加新的功能,从中我学到了很多:
1.熟练掌握 Java 集合、字符串解析、正则表达式、多轮迭代仿真、输入文本处理等 Java 语法;
2.完整建立抽象类、继承、多态的面向对象分层设计思路,理解开闭原则;
3.深刻认识到前期架构设计对代码可拓展性的重要,过程式代码迭代重构成本极高;
4.学会搭建自动化测试用例,覆盖多级级联电路、边界引脚、输入冲突、非法语句等场景。
Complexity Metrics(复杂度分析)
因为下面要用到复杂度分析,所以先在此给出一些相关概念。
我们需要使用的主要是方法和类的复杂度分析。
方法的复杂度分析主要基于循环复杂度的计算。循环复杂度是一种表示程序复杂度的软件度量,由程序流程图中的 “基础路径” 数量得来。
ev (G):即 Essentail Complexity,用来表示一个方法的结构化程度,范围在 [1,v (G)]之间,值越大则程序的结构越 “病态”,其计算过程和图的 “缩点” 有关。
iv (G):即 Design Complexity,用来表示一个方法和他所调用的其他方法的紧密程度,范围也在 [1,v (G)]之间,值越大联系越紧密。
v (G):即循环复杂度,可以理解为穷尽程序流程每一条路径所需要的试验次数。
对于类,有 OCavg 和 WMC 两个项目,分别代表类的方法的平均循环复杂度和总循环复杂度。
下面我将从程序结构,公测、互测以及 bug 分析几个方面来总结本单元的三次作业。
(1)作业要求
基础引脚信号管理:通过 INPUT 语句定义全局输入引脚初始电平;
导线连接规则:[] 包裹导线语句,首个引脚为信号输出源,后续全部为目标输入引脚;
五类基础逻辑门仿真:与门 A、或门 O、非门 N、异或门 X、同或门 Y;
A、O 支持自定义多路输入,格式为 A (输入数) 编号;N/X/Y 固定引脚数量;
电路仿真机制:循环迭代同步导线电平,不断更新门电路输出,直至电路信号稳定不再变化;
输出规范:严格按照 A→O→N→X→Y 顺序打印,同类元件按编号升序输出,引脚输入不全、无法计算输出的元件忽略不打印。
实现方式
输入逐行读取,读到 end 结束输入;
解析流程分为两步:解析全局输入引脚、解析所有导线并自动生成对应逻辑门对象;
仿真分为两轮循环:第一轮同步所有导线传递电平,第二轮遍历全部逻辑门读取输入、计算输出并更新引脚信号;
整体采用基础封装设计,划分 PinSignal、CircuitWire、LogicGate 三类实体,Main 作为入口统筹输入、仿真、排序输出,门电路运算使用多分支 if-else 判断,无抽象与继承。
代码规模
第一次作业代码规模如下图。

(2)类图

(3)复杂度分析
第一次作业的复杂度分析如下。




代码解析
(1). 引脚、导线、逻辑门实体类

解析:将引脚、导线、逻辑门拆分为独立类,实现基础数据封装,把数据与简单存取方法绑定,符合基础面向对象封装思想;但所有门运算逻辑全部写在 Main 主方法,实体类仅做数据存储,没有业务计算能力。
(2). 导线电平同步循环

解析:采用迭代收敛仿真机制,设定最大 20 轮循环防止反馈电路死循环;每次导线电平发生修改就标记 changed,若无更新则代表电路电平稳定,退出仿真循环,贴合数字电路信号逐级传递的硬件特性。
(3). 多类型门电路分支计算

解析:使用大量 if-else 分支区分五类门电路运算逻辑,新增元件时必须修改此处代码,严重违反开闭原则;代码耦合度高,Main 方法 v (G) 循环复杂度偏高,结构臃肿。
(4). 元件排序输出

解析:先按元件类型固定顺序筛选,内部使用选择排序实现编号升序,满足作业输出格式要求;排序逻辑写在主流程中,未单独封装工具类,复用性差。
(4)测试验证与 Bug 复盘
测试方法
手动构造多级级联电路、多输入与 / 或门、反馈循环电路;批量构造边界用例:引脚缺失、无输入导线、无输出导线、单元件电路;人工演算输出结果,与程序打印内容比对。
Bug 复盘
公测 Bug:迭代上限 20 轮不足,复杂反馈电路电平无法完全收敛,输出结果错误;
互测被 Hack:导线重复绑定同一目标引脚,出现信号冲突未做判断,引脚电平反复覆盖;
主动 Hack:多次测出他人代码缺陷,核心问题集中在引脚字符串解析逻辑错误、门编号排序遗漏、缺少输入引脚校验导致空指针。
(5)经验总结
本次作业完成基础数字电路仿真,核心收获是熟悉集合容器、字符串分割、循环仿真流程,初步理解封装思想;不足十分明显:全程过程式编程,使用巨型 if-else 区分元件,没有抽象分层,拓展性极差;主方法代码量巨大,循环复杂度高,后续新增器件、复合元件需要大规模重构,为第二次作业埋下架构缺陷。
第二次作业
(1)作业要求
元件拓展:新增三态门 S、译码器 M、数据选择器 Z、数据分配器 F,元件扩充至九类;
差异化引脚规则:新增控制引脚、多路输出引脚、高阻无效状态;
特殊输出格式:译码器输出选中编号、分配器输出带 "-" 无效标记字符、三态门单独输出引脚;
导线与仿真机制复用原有逻辑,迭代上限提升至 30 轮;
输出顺序更新为 A→O→N→X→Y→S→M→Z→F,不同器件打印对应输出引脚。
实现方法
保留 Pin、Wire 基础实体,统一使用万能 Device 类存储所有九类元件;
使用 Object 类型存储差异化输出(整数、字符串、数字索引),兼容不同器件返回值;
解析逻辑复用第一次作业文本分割规则,新增分支处理 S/M/Z/F 四类器件引脚读取;
仿真流程不变,仅扩展门运算分支,新增各类复合器件输入读取、运算逻辑;
Main 承载全部器件计算逻辑,无继承、无抽象类。
代码规模
第二次作业代码规模如下图。

(2)类图

复杂度分析
第二次作业的复杂度分析如下。




(4)代码解析
(1). 统一器件实体 Device 类

解析:用单一万能类承载所有九类器件,通过 type 字段区分类型,Object 兼容不同输出类型,省去多个实体类;但违背单一职责,一个类承载所有器件数据,耦合严重。
(2). 复合器件运算分支(核心新增代码)

解析:在原有五段分支基础上新增四段复合器件判断,Main 中 if-else 分支进一步膨胀,v (G) 循环复杂度大幅上升;每新增一类器件都需要修改此处代码,维护成本高。
(3). 差异化输出打印

(4)测试验证与 Bug 复盘
测试方法
沿用第一次自动化测试思路,新增译码器、三态门、多路选择 / 分配器专用边界用例;构造多位选择位、多输出引脚场景测试。
Bug 复盘
公测 Bug:分配器高低位反转,输出标记字符顺序颠倒;
互测被 Hack:控制引脚编号读取顺序错误,三态门高阻逻辑失效;
主动 Hack:成功发现他人译码器二进制计算错误、迭代次数不足导致多级器件电平不收敛。
(5)经验总结
本次作业拓展九类器件,完成复合数字元件仿真;核心收获是学会多类型数据兼容存储、多输出引脚处理;但暴露了无抽象架构的致命缺陷:巨型分支导致代码复杂度飙升,新增器件需要多处修改代码,可维护性极差,意识到抽象类、多态是消除分支、解耦逻辑的核心方案,为第三次重构提供明确方向。
第三次作业
(1)作业要求
子电路拓展:支持 C 编号定义子电路,子电路内置 INPUT/OUT 端口、独立内部导线与门电路,主电路可引用子电路端口,子电路元件输出带 C 编号前缀;子电路拥有独立作用域,单独仿真计算;
输入异常校验:五级优先级导线冲突检测,出现重复驱动同一引脚直接输出 ERROR 并终止程序;
架构大规模重构:抽象 Gate 基类,AndGate、OrGate、NotGate、XorGate、XnorGate 子类重写 calculate () 方法,利用多态消除巨型 if-else 分支;
全局信号冲突检测:同一引脚被多条导线驱动直接报错,提升程序鲁棒性。
实现方法
严格遵循单一职责原则,拆分多层类:
1.抽象 Gate 基类,定义通用引脚、输入读取、抽象计算方法;五类基础门继承并重写计算逻辑;
2.SubCircuit 子电路类,独立存储内部门、内部导线、输入输出端口、端口绑定电平;
3.Main 仅负责文本解析、全局仿真调度、异常检测、输出排序,不再承载任何门运算逻辑;
4.所有器件计算逻辑下沉至对应子类,通过多态统一调用 calculate (),消除大量条件分支。
代码规模
第三次作业代码规模如下图。

(2)类图

(3)复杂度分析
第三次作业的复杂度分析如下。

(4)代码解析
(1). 抽象门基类与子类多态实现

解析:使用抽象类 + 多态重构核心运算逻辑,每类门单独实现计算方法,新增器件仅需新增子类,无需修改主流程代码,完美符合开闭原则;Main 中遍历 Gate 调用 calculate (),彻底删除巨型 if-else,大幅降低 Main 方法 v (G) 循环复杂度。
(2). 输入校验优化(InputValidator 类)

解析:单独抽取输入校验工具类,统一处理所有非法输入(负数、超出范围数字),出现非法值直接打印提示并终止程序;将输入校验逻辑从 Main 剥离,简化主流程,提升代码复用性与鲁棒性。
(3). 子电路 SubCircuit 类实现

解析:子电路独立封装内部全部元件、导线、端口绑定逻辑,拥有独立仿真函数,实现电路复用;内置导线冲突校验,解析阶段提前捕获输入错误,程序鲁棒性大幅提升。

解析:Main 不再包含任何门运算逻辑,仅做调度、数据分发、异常判断,方法复杂度显著降低;顶层电路、子电路分层仿真,逻辑清晰,职责单一。
(4)测试验证与 Bug 复盘
测试方法
搭建完整自动化测试脚本,批量生成包含子电路、多级嵌套、引脚冲突、非法导线、多反馈回路的测试用例;区分正常输入、异常输入两类场景,自动捕获 ERROR 输出与标准输出比对。
Bug 复盘
公测 Bug:子电路端口电平同步延迟,多层嵌套子电路迭代收敛速度慢;
互测表现:全程未被测出 Bug,主动 Hack 成功发现他人 3 处核心缺陷:导线冲突未检测、抽象类未重置计算标记、子电路输出端口未缓存电平。
(5)经验总结
本次作业完成架构重构,实现子电路复用、输入异常校验;核心收获是彻底掌握抽象类、继承、多态的实际工程价值,理解单一职责、开闭原则如何优化代码复杂度;通过三次迭代对比,直观感受到前期架构设计对拓展性的决定性作用;不足在于子电路解析逻辑仍存在少量嵌套分支,输入校验工具类可进一步拆分,后续可以通过工具类抽取进一步降低主方法代码量。
4. 关于面向对象设计与可拓展性的思考
数字电路三次迭代作业是 Java 面向对象单元完整的实践项目,三次版本从过程式万能类,到抽象分层多态架构,迭代过程直观展示了糟糕设计带来的重构成本。
第一次、第二次作业为快速实现功能,采用单一实体承载全部器件、主方法堆砌大量条件分支,每新增一类元件、一种输出格式,都需要修改多处代码,循环复杂度持续走高,拓展、维护难度极大;第三次通过抽象基类 + 多态拆分运算逻辑,新增器件仅需新增子类,无需改动原有调度代码,同时拆分子电路独立作用域,实现电路模块复用,输入校验统一封装,代码分层清晰、职责明确。
整套单元练习让我建立起完整的工程化开发思维:开发前需要提前规划分层架构,使用抽象、多态隔离变化点,遵循单一职责与开闭原则;同时自动化测试是保障迭代质量的关键,提前覆盖边界、冲突、非法输入场景,能大幅减少公测与互测暴露的漏洞。虽然代码仍存在优化空间,但三次迭代的对比让我直观体会到面向对象设计的工程意义,为后续复杂项目开发积累了宝贵经验。