面向对象作业集 4~6 阶段性总结 —— 数字逻辑电路仿真迭代开发复盘

一、前言

 
本次作业集 4、5、6 是一套连续迭代的数字逻辑电路仿真 Java 程序开发任务,三份代码 Test1、Test2、Test3 依次对应三次作业,全程围绕数字电路输入信号、各类逻辑元件、线路连接、电平仿真计算这一统一业务场景,循序渐进拓展功能、重构代码架构,完整复刻软件行业 “最小可用版本→功能迭代升级→分层架构重构” 的开发流程,是面向对象思想从理论落地到工程实践的整套训练。
 

1. 三次作业知识点递进关系

 
作业 4(Test1)为基础入门版本,核心考察 Java 基础面向对象封装、枚举类使用、ArrayList/HashMap 集合操作、控制台多行文本解析、简单循环迭代仿真算法。仅实现与、或、非、异或、同或 5 种基础单输出逻辑门,电路结构仅单层,无子电路、无复杂复合器件、无输入错误检测。
 
作业 5(Test2)在基础门之上拓展复合数字器件,新增三态门、译码器、数据选择器、数据分配器,引入控制引脚、多路输入、多路输出概念,考察对象组合聚合关系、多分支条件计算、引脚结构建模、多输出格式化输出逻辑,对程序分支处理能力要求大幅提升。
 
作业 6(Test3)是综合完整版,新增子电路分层定义、子电路端口映射、线路语法错误检测、多驱动冲突拦截功能,重点考察分层对象设计、嵌套结构处理、输入鲁棒性校验、标准化错误输出,完整覆盖复杂分层系统建模、异常流程处理等工程化知识点。
 

题量、代码规模与难度梯度

 
三次作业均为单道综合编程大题,无拆分小题,但代码体量逐次翻倍:
 
  1. 作业 4:有效代码 216 行,仅 Gate 内部类 + Main 主类 2 个核心结构,难度★★,仅需处理单层简单电路;
  2. 作业 5:有效代码 447 行,重构 Gate 类,新增引脚数组、器件类型常量,难度★★★,需区分控制 / 数据引脚,处理多输出器件;
  3. 作业 6:有效代码 592 行,新增独立 SubCircuit 子电路类,拆分大量校验、取值工具方法,难度★★★★,嵌套子电路与多类错误检测大幅增加调试成本。
作业 核心知识点 题量 难度
4 仅 Gate 内部类 + Main 主类 2 个核心结构,仅需处理单层简单电路;  216 行 ★★
5 重构 Gate 类,新增引脚数组、器件类型常量,需区分控制 / 数据引脚,处理多输出器件;  447 行 ★★★
6 新增独立 SubCircuit 子电路类,拆分大量校验、取值工具方法,嵌套子电路与多类错误检测大幅增加调试成本。  592 行 ★★★★
整体难度呈现稳步上升趋势,前期只需要实现电路计算功能,后期不仅要完成运算,还要兼顾输入合法性、多层电路信号传递、标准化报错,对代码严谨性、可扩展性、容错能力提出更高要求。整套作业没有零散小知识点,全部围绕 “电路对象建模” 串联,能直观感受迭代开发中旧代码重构、功能兼容的完整过程。
 

二、设计与分析

 
本章节结合三份源码的类结构、PowerDesigner 文字化类图、SourceMonitor 代码复杂度报表,逐次分析三次作业的设计思路、结构优缺点与个人开发心得,文中不粘贴完整源码,仅从架构层面拆解设计逻辑。
 

2.1 作业 4(Test1)基础逻辑门仿真设计分析

 

2.1.1 UML 类结构(PowerDesigner 建模)

 
整体结构极简,仅两层结构:
屏幕截图 2026-06-23 162424
  1. 外部主类 Main:程序唯一入口,承担输入读取、线路解析、元件创建、迭代计算、排序输出全部流程;
  2. 静态内部类 Gate:单一实体类,聚合字符串数组 sourceNames 存储各引脚驱动源,布尔 outputValue 存储输出电平;搭配枚举 GateType 区分 5 种基础门类型;
     
    对象存储容器:Map<String, Boolean>存放顶层输入信号,LinkedHashMap<String, Gate>存储所有逻辑门,保证遍历有序。
     
    类之间仅存在 Main 对 Gate 的依赖关系,无继承、多态设计,所有业务逻辑全部耦合在 main 主方法内。
 

2.1.2 SourceMonitor 复杂度度量数据

 
表格
屏幕截图 2026-06-23 162929
 
 
Method ev(G) iv(G) v(G)
Main.main 3 8 5
Gate 构造方法 1 1 1
枚举匹配门计算逻辑 2 4 4
类整体度量      
类名 WMC 总复杂度 OCavg 平均方法复杂度  
------ ------------ --------------------  
Main 36 3.2  
Gate 4 1.0  
 
数据解读:main 方法 v (G)=5,是全程序复杂度最高的方法,原因是串行执行读取输入、解析线路、创建门、循环迭代计算、排序输出五大模块,大量 if、for 循环嵌套;Gate 实体类仅做数据存储,无业务逻辑,复杂度极低,符合基础封装要求。
 

2.1.3 设计实现与个人心得

 
本次设计思路是 “先实现功能,再考虑结构”,刚接触面向对象,仅把门抽象为独立实体存储名称、引脚、输出值,没有拆分工具类,所有解析、计算、输出代码全部堆在 main 里。仿真算法采用暴力循环迭代:反复遍历所有门,只要某一扇门所有输入信号全部已知,就计算输出电平,直到一轮循环没有任何门更新数值,判定电路达到稳态后排序输出。
 
优点:实体与业务数据做了基础封装,用枚举区分门类型,计算逻辑清晰;
 
缺陷极其明显:单一主类职责过载,完全不符合单一职责原则(SRP),输入解析、仿真计算、输出打印完全混杂,后续拓展复合器件时修改成本极高,任何一处逻辑改动都要在几百行 main 里寻找对应代码。
 

2.2 作业 5(Test2)复合器件仿真设计分析

 

2.2.1 UML 类结构(PowerDesigner 建模)

屏幕截图 2026-06-23 162505
在 Gate 实体类上完成重构,不再只有单一输入输出,新增三类引脚数组ctrlPins控制引脚、inPins数据输入引脚、outPins输出引脚,同时用静态常量区分 9 类器件(基础门 + 三态 / 译码 / 选择 / 分配器);新增静态工具方法getSourceValue统一从信号或其他门读取电平,消除重复取值代码。
 
类结构仍只有 Main+Gate 两层,但 Gate 内部数据结构大幅扩充,main 中拆分出少量取值逻辑作为静态方法,一定程度解耦;对象容器沿用信号 Map、门 LinkedHashMap,新增 int 数组存储多输出电平状态,支持译码器、分配器输出 “-” 无效标识。
 

2.2.2 SourceMonitor 复杂度度量数据

屏幕截图 2026-06-23 162939
表格
 
 
 
Method ev(G) iv(G) v(G)
Main.main 4 11 7
Gate.buildPins 引脚初始化 2 6 6
getSourceValue 取值工具 1 3 3
器件分支计算逻辑 3 7 6
类整体度量      
类名 WMC 总复杂度 OCavg 平均方法复杂度  
------ ------------ --------------------  
Main 62 3.7  
Gate 19 1.6  
 
数据解读:WMC 从 36 上涨至 62,核心原因是新增 4 类复合器件后,计算部分巨型 switch 分支,每种器件独立一套电平计算规则,方法间调用依赖增多,iv (G) 设计复杂度显著升高;buildPins方法针对 9 类器件分别初始化引脚编号,多重 case 判断拉高循环复杂度。相比作业 4,抽离出统一取值工具,消除了大量重复代码,结构有小幅优化。
 

2.2.3 设计实现与个人心得

 
本次开发吸取上次 main 臃肿的教训,把重复读取电平的代码抽成独立静态方法,同时重构 Gate 类适配多引脚、多路输出器件,能区分控制信号与数据信号,三态门、译码器的特殊逻辑可以统一在 Gate 内部处理。
 
但本次设计仍存在核心短板:没有使用继承、多态拆分各类器件,所有门、译码器、选择器全部放在同一个 Gate 类中,依靠超长 switch 区分运算规则,新增器件时必须修改 buildPins、计算两大块代码,违反开闭原则(OCP);同时输入线路解析、器件名称匹配、输出打印依旧全部留在 main 中,主类负载依旧很重。
 

2.3 作业 6(Test3)子电路分层仿真设计分析

屏幕截图 2026-06-23 162528

2.3.1 UML 类结构(PowerDesigner 建模)

 
首次实现分层对象建模,新增独立SubCircuit子电路类,单独存储子电路输入端口、输出端口、内部线路、内部所有门;顶层 Main 同时管理主电路元件和全部子电路模板,拆分独立静态校验方法checkConnectionError检测线路语法、信号冲突,computeGate单独封装单门计算逻辑,大幅减轻 main 负担。
 
对象容器新增子电路 Map,子电路内部拥有独立 Gate 集合;门引脚结构沿用作业 5 设计,新增子电路端口字符串映射规则(C1-xx 格式),计算时需要先递归同步子电路电平至顶层。
 

2.2.2 SourceMonitor 复杂度度量数据

屏幕截图 2026-06-23 163043
表格
 
 
 
Method ev(G) iv(G) v(G)
Main.main 4 9 6
SubCircuit 子电路解析 3 8 7
checkConnectionError 线路校验 4 5 5
computeGate 门计算 2 5 4
类整体度量      
类名 WMC 总复杂度 OCavg 平均方法复杂度  
------ ------------ --------------------  
Main 78 4.1  
Gate 22 1.8  
SubCircuit 26 3.2  
 
数据解读:新增 SubCircuit 独立类拆分分层逻辑,main 的单一业务压力有所缓解,但子电路端口映射、线路错误检测新增大量分支,整体 WMC 继续上涨;checkConnectionError方法需要判断线路单信号、多驱动、顺序错误等 5 类问题,分支较多;分层设计后代码职责划分更清晰,不再所有数据、逻辑堆在同一个类中,是三次作业架构最优版本。
 

2.3.3 设计实现与个人心得

 
本次迭代最大进步是理解分层抽象的意义,将可复用的子电路单独建模,实现主电路、子电路两套独立电路环境,同时把错误校验、门计算拆分为独立静态工具,main 仅负责读取输入、调度整体流程。开发中意识到程序鲁棒性的重要性,按照题目要求检测各类线路错误并标准化输出 ERROR 提示。
 
但架构依旧存在明显缺陷:未使用自定义异常类,全部靠打印字符串提示错误;子电路端口映射采用大量字符串 if 判断,硬编码过多;各类器件依旧共用 Gate 类,没有通过多态分离运算逻辑,新增器件仍需要修改多处代码。
 

三、采坑心得

 
三次作业开发、自测、强测过程中出现大量 Bug,所有问题均结合具体测试用例、运行现象、代码结构定位根源,不空谈理论,以实测数据和代码结构作为分析依据。
 

3.1 作业 4(基础门)典型问题

 

坑 1:门输出引脚固定后缀 - 0 判断缺失,迭代计算死循环

 
测试用例输入
 
INPUT:A-1 B-0
 
[A(2)1 A(2)1-1 X1-0]
 
end
 
现象:程序无限循环,无任何输出,控制台卡死。
 
定位过程:逐行打印每扇门的输入源取值,发现 A (2) 1-1 被识别为普通信号名,无法匹配门对象,返回 null,导致该门输入始终存在未知电平,循环永远存在更新标记。
 
根源:代码中判断门输出源时,仅简单分割 “-”,没有强制判断后缀必须为 0,门的输出引脚统一规定为 xx-0,其余数字引脚是输入,未做过滤逻辑。
 
修复方案:解析源字符串时,分割短横线后判断后半段字符必须为 0,否则判定为非法驱动源,跳过该引脚赋值。
 

坑 2:枚举排序顺序与题目输出要求不符

 
题目规定输出顺序:与→或→非→异或→同或,代码中 GateType 枚举定义顺序颠倒,排序后输出元件顺序完全错乱,强测大量格式失分。
 
根源:排序直接调用枚举 ordinal (),没有自定义 Comparator 重写器件排序优先级,简单修复自定义比较器,按题目给定顺序映射数字。
 

坑 3:循环迭代无最大迭代上限

 
复杂链式电路(多级门层层依赖)会出现迭代上千次仍未收敛,程序超时卡死,代码仅靠 changed 布尔值退出,无循环次数保护。
 

3.2 作业 5(复合器件)典型问题

 

坑 1:三态门控制引脚逻辑混淆,高阻态失效

 
测试用例
 
INPUT:CON-0 DATA-1
 
[S1 CON-0 S1-1]
 
end
 
预期:控制信号 CON 为 0,三态门断开,输出无有效值;
 
实际运行:程序正常输出 DATA 电平,无高阻标识。
 
根源:代码区分 ctrlPins 与 inPins 时,取值逻辑写反,控制信号读取错误,没有判断控制电平 0 时 active 置 false。
 
调试过程:打印 ctrlVals 数组,发现控制引脚读取到的是数据信号,调换引脚数组读取顺序后修复。
 

坑 2:译码器二进制索引计算颠倒

 
输入两位数据 1、0,理论索引为 2,代码移位时低位在前,算出索引 1,输出 0 的引脚完全错位,强测译码用例全错。根源:遍历输入数组时从后向前移位,循环遍历顺序颠倒,调整循环方向后解决。
 

坑 3:同类型元件重复编号仅丢弃无提示

 
输入两个 N1 非门,程序直接覆盖后创建第二个门,无任何提示,题目要求检测同类型同编号元件冲突,代码缺少冲突检测逻辑,评测隐性用例失分。
 

3.3 作业 6(子电路完整版)典型问题

 

坑 1:子电路内外端口映射逻辑错误,信号无法跨层传递

 
测试用例:先定义 C1 子电路,内部 X1 门连接子输入 IN,主电路将顶层信号 S 接入 C1-IN;运行后 X1 输入始终为未知 null。
 
根源:解析主电路线路时,识别 C1-IN 子端口后,没有递归遍历子电路内部所有输入引脚绑定的门,仅修改顶层映射,子电路内部元件取值没有同步更新。
 
修复:识别子端口驱动源时,遍历对应子电路所有连接该端口的门,同步填充输入源名称。
 

坑 2:线路多驱动冲突未拦截,无 ERROR 输出

 
同一门输入引脚绑定两个不同信号源,程序正常计算,未检测冲突,不符合题目错误检测要求。根源:checkConnectionError仅判断线路首尾顺序,缺少对目标引脚重复绑定的哈希表记录逻辑,新增destSources哈希存储每个引脚所有驱动源,长度大于 2 则判定冲突。
 

坑 3:多层嵌套子电路迭代次数不足

 
两层、三层子电路嵌套时,单次迭代仅更新一层电平,循环上限 200 次不足以完成全部层级计算,深层门输出始终为 null,加大最大迭代阈值至 500 后解决。
 

坑 4:子电路元件输出缺少 Cx - 前缀

 
子电路内部门输出打印时,没有拼接子电路 ID 前缀,评测系统判定输出格式错误,遍历子电路门输出时增加前缀字符串拼接逻辑。
 

共性踩坑总结

 
三次作业出现的 Bug 有统一规律:一是字符串解析缺少边界判断(短横线、子电路标识、引脚数字),二是分层 / 多对象场景下数据同步逻辑缺失,三是前期代码拆分不到位,巨型方法中分支过多,调试时很难定位对应逻辑;同时前期完全没有自测用例,仅靠简单样例调试,大量边界场景覆盖不全。
 

四、改进建议

 
结合三次作业代码的结构缺陷、复杂度问题、Bug 根源,分单次迭代给出针对性可持续优化方案,同时提供通用长期重构思路,兼顾短期修复与长期工程化拓展。
 

4.1 作业 4 基础门代码改进方案

 
  1. 代码拆分重构:将输入解析、电路迭代计算、元件排序输出拆分为三个独立静态工具类InputParser、CalcEngine、OutputPrinter,Main 仅做流程调度,彻底解决主类过载问题,降低 main 循环复杂度 v (G);
  2. 迭代循环防护:设置最大迭代次数常量 MAX_LOOP=500,达到上限直接终止并打印电路存在环路提示,避免程序卡死;
  3. 封装排序工具:独立静态方法实现器件自定义排序,不依赖枚举原生顺序,适配题目输出规则;
  4. 输入校验补充:增加信号、门名称正则校验,非法字符输入提前拦截,提前输出提示。
 

4.2 作业 5 复合器件代码改进方案

 
  1. 引入多态重构 Gate 类:删除单一巨型 Gate,新建抽象父类 LogicElement,分别实现 AndGate、Decoder、Tristate 等子类,各自重写 calc () 计算方法,消除上万行 switch 分支,大幅降低 iv (G) 设计复杂度;
  2. 引脚操作封装工具:新建 PinUtil 工具类,统一区分控制 / 数据引脚、读取引脚电平,消除重复引脚判断代码;
  3. 增加编号冲突检测:创建 Map 记录每类器件已使用编号,创建 Gate 前查重,冲突直接打印 ERROR 并终止;
  4. 输出格式化统一:新建 PrintUtil,统一处理单输出、多输出、高阻 “-” 格式,分散打印逻辑集中管理。
 

4.3 作业 6 子电路完整版改进方案

 
  1. 自定义异常体系替代打印 ERROR:创建LineConflictException、IllegalPinException、SubCircuitPortError等业务异常,try-catch 捕获后统一输出标准化错误,替代零散 System.out 打印,代码整洁度大幅提升;
  2. 子电路映射抽离工具类:新建 SubPortMapper,专门处理主电路与子电路端口信号同步,剥离 main 与 SubCircuit 中的映射分支;
  3. 拓扑排序替代暴力迭代:先构建电路信号依赖拓扑图,按依赖顺序计算元件电平,无需多次循环迭代,提升计算效率;
  4. 正则常量统一管理:新建 RegexConst 类,存储门名称、子电路、线路匹配正则,消除代码中大量硬编码字符串;
  5. 错误校验模块化:将 5 类线路校验拆分为独立小方法,每个方法仅检测一类错误,降低单方法 ev (G) 结构化复杂度。
 

长期通用可持续优化方案

 
  1. 工厂模式创建器件:新增 GateFactory 工厂类,根据名称字符串自动实例化对应器件子类,消除大量 if-else 名称解析代码;
  2. 补充单元测试:使用 JUnit 编写测试用例,覆盖基础门、译码器、子电路、错误线路场景,自动化校验输出,减少人工自测成本;
  3. 访问者模式遍历电路:新增访问者接口,统一实现计算、打印、校验三大遍历逻辑,新增功能无需修改电路主体类;
  4. 配置常量抽离:迭代上限、器件排序优先级、引脚标识(-0)全部定义静态常量,修改需求无需遍布全代码查找字符串。
 

五、综合性总结

 

5.1 本阶段学习收获

 
经过三次迭代电路仿真作业,我从只会写面向过程代码、简单封装实体,逐步建立完整面向对象工程思维,收获分为代码能力、设计思维、工具使用三方面:
 
  1. Java 编码与业务建模能力:熟练使用 HashMap、ArrayList、LinkedHashSet 各类容器处理电路信号与元件;掌握多行文本、复杂线路字符串解析技巧,能通过分割、正则匹配提取业务信息;学会循环迭代仿真算法,理解数字电路电平稳态计算逻辑,能处理多输出、分层嵌套复杂对象关系。
  2. 面向对象设计核心思想落地:完整体会封装、聚合、分层抽象的价值,明白单一职责原则对代码维护的意义;区分实体类、工具类、流程调度类的职责边界;通过三次重构直观感受到迭代开发中 “旧代码兼容、功能拓展” 的难点,理解开闭原则的实际作用。
  3. 代码质量量化与测试思维:学会使用 SourceMonitor 分析圈复杂度 v (G)、总权重 WMC、平均复杂度 OCavg,能通过数据定位臃肿、分支过多的高风险方法;能自行构造边界测试用例,定位字符串、分层同步、分支判断类隐性 Bug,不再仅依赖样例调试。
 

5.2 现存短板与后续学习方向

 
  1. 设计模式运用薄弱:全程没有使用多态、工厂、访问者等成熟设计模式,所有器件共用一个实体类,依靠大量分支区分逻辑,代码耦合度高,后续系统学习工厂、策略、组合模式并重构电路代码练习;
  2. 算法优化不足:仿真仅采用暴力循环迭代,未使用拓扑排序优化计算顺序,复杂电路运行效率偏低,后续学习图拓扑相关算法;
  3. 测试体系缺失:全程仅人工黑盒自测,没有编写自动化单元测试,边界场景极易遗漏,后续系统学习 JUnit 测试框架;
  4. 异常处理简陋:全部靠打印字符串提示错误,没有分层异常体系,不符合工业级代码规范,学习 Java 自定义异常、异常分层处理。
 

5.3 课程、教学改进建议

 
  1. 案例结合讲解设计模式:课堂单纯讲抽象类、多态理论很难理解,可结合本次数字电路仿真案例演示工厂、组合模式的改造前后对比,直观体现设计模式降低复杂度的作用;
  2. 前置字符串解析实操教学:三次作业最大坑集中在线路、元件名称字符串处理,课前补充正则、文本分割实操练习,减少调试耗时;
  3. 配套标准测试用例:每次作业发布时提供基础、边界、错误三类标准输入样例,方便学生自测,减少强测大量格式、逻辑失分;
  4. 增设代码互评环节:作业完成后组织同学互相阅读代码,分析对方类结构、复杂度缺陷,互相借鉴拆分、解耦思路;
  5. 增加代码度量实操课:课堂演示 SourceMonitor 工具使用,结合作业代码讲解 v (G)、WMC 指标含义,教学生根据复杂度数据重构臃肿方法。
 

5.4 整体感悟

 
三次电路仿真迭代作业是完整的小型软件项目训练,从最开始只追求 “能跑通”,到后期主动拆分模块、考虑程序容错、用工具量化代码质量,思维从单纯 “写代码完成题目” 转变为 “设计一套可拓展、易维护的系统”。迭代过程中反复重构旧代码、修复分层同步、字符串解析类隐性 Bug,让我深刻意识到:好的面向对象设计不是一蹴而就,而是持续迭代、持续解耦、持续优化的过程。本次积累的分层建模、文本解析、复杂度优化、鲁棒性校验思路,对后续更复杂的大型程序开发有极大参考价值。
posted @ 2026-06-23 16:56  沈诗棋  阅读(21)  评论(0)    收藏  举报