阶段性编程作业 4~6 综合复盘博客:
阶段性编程作业 4~6 复盘博客:数字电路仿真小程序三次迭代学习记录
前言
这段时间连续完成了第 4、5、6 三次 Java 面向对象编程作业,三个题目其实是同一个电路模拟小程序不断升级改版,难度一点点往上加,越往后代码越复杂,踩的坑也一次比一次多。简单梳理下三次作业整体情况:
作业 4 是最基础的版本,只需要实现五种简单逻辑门:与门、或门、非门、异或、同或,输入电路连线文字,程序算出每个门输出电平。知识点主要学抽象类、继承、链表容器,代码量不算大,难度偏低,刚学完面向对象就能上手写。
作业 5 在 4 的基础上大幅扩容,新增三态门、译码器、多路选择器、多路分配器这些复杂器件,而且原来的门只能一个输出,新版元件支持多输入多输出。同时优化了信号传递逻辑,用队列代替反复循环扫描电路,知识点多了队列、泛型链表、复杂分支运算,难度中等偏上,写的时候经常出现信号传不动、输出乱码的问题。
作业 6 是最终完整版,也是最难的一次,新增了子电路嵌套功能,支持自定义子电路块,还要额外加语法检查,写错连线格式程序要主动报错。用到了哈希表、哈希集合、自定义存储类、字符串前缀处理、多层文本解析,代码量是三次里最多的,调试时间最长,好几次一跑全是报错,改到很晚才通过全部测试样例。
三次作业是递进更新的关系,后面的代码要兼容前面所有功能,每一次都要在旧代码基础上改结构、加功能、修 bug,刚好完整体验了 “先写基础版本,再迭代升级” 的开发流程,也暴露出我自己编码、逻辑思考上很多不足,下面结合每次写的代码、软件分析截图详细复盘。
一、三次作业代码设计与个人分析
(一)作业 4 基础简易门电路模拟器
- 代码结构简单介绍

一共九个类,结构很清晰,一层套一层:
InterFace 引脚类:相当于电路里一根根接线头,存名字和高低电平 0/1,还记录自己要传给哪些别的引脚;
Door 抽象父类:所有门的统一模板,规定所有门都要有输入引脚、一个输出引脚,还有必须实现计算输出的 run () 方法;
A/O/N/X/Y 五个子类:分别对应五种基础门,重写 run 方法,写清楚各自的运算规则;
Main 主类:程序入口,读取输入文字、创建门、连接引脚、循环计算电平、最后打印结果。
PowerDesigner 画的类图结构很直观:引脚和门是包含关系,五种门全部继承父类 Door,主程序用两个链表装所有门和引脚。
2. SourceMonitor 代码分析软件数据感受

用软件扫描这份代码后,能直观看到自己写代码的坏习惯:
总代码四百多行,大部分逻辑全堆在 main 主函数里,这个方法复杂度是全代码最高的。查找引脚的方法是从头到尾遍历链表,引脚一多查找速度会变慢。
同时当初设计父类 Door 的时候直接写死只能有 1 个输出,当时只想着满足作业要求,完全没考虑后面要做多输出器件,为作业 5 大规模重构埋下麻烦。
3. 写完这份代码的心得
刚学继承和抽象类,第一次写分层结构程序,最大收获是弄懂了抽象父类的用处:把所有门共用的代码(创建引脚、判断输入是否全部有电平)全部抽出去,不用五个门重复写一模一样的代码。
但设计思维特别稚嫩,只盯着当下题目需求,完全没有预留拓展空间,而且所有数据都用链表存,查找全靠遍历,效率很低,也不懂把复杂功能拆成独立小方法,main 函数臃肿难调试。
(二)作业 5 多功能器件升级版本
- 代码重构改动

吸取作业 4 的教训,直接把顶层父类 Door 改成 Component,去掉只能单输出的限制,输入、输出引脚都用链表存储,能自由设置输入、输出数量。
新增了三态门、译码器、多路选择器、多路分配器,每个器件单独写一个类,各自实现 compute 计算方法。
信号传递改掉之前反复循环扫描所有门的笨办法,改用队列:只要一个门输入全部就绪,就丢进队列,依次取出计算,计算完再更新下游引脚,不用每一轮遍历全部电路。
2. 工具分析对比

对比作业 4,这份代码类数量涨到 13 个,拆分了很多独立小方法,main 函数代码量减少,复杂度下降。
但还是存在问题:存储引脚依旧只用链表,没有用哈希表快速匹配引脚名称;创建元件、解析文本的逻辑还是耦合在一起,代码条理不够清爽;译码器、选择器里位运算写得很乱,调试时很难看懂数值变化。
3. 本次迭代心得
这次明显体会到 “前期设计不足,后期重构加倍麻烦”。因为作业 4 父类限制单输出,这次几乎重写了顶层父类所有代码,浪费很多时间。
队列优化信号传播是很大的进步,之前循环扫描经常出现重复计算、信号死循环,队列有序处理电路,运行逻辑更贴合真实电路信号传递流程。
同时也发现自己对位运算、多路寻址逻辑不熟练,写译码器地址计算时反复测试才调对。
(三)作业 6 支持子电路 + 语法校验完整版
- 新增核心结构

在作业 5 所有器件基础上,新增 SubCircuitDef 类,专门用来存子电路的输入、输出、内部连线;
容器升级,改用 HashMap 存所有引脚,通过引脚名字直接匹配,不用遍历链表;用 HashSet 保存顶层输入引脚,方便语法判断;
新增一大段语法检测代码:如果连线写反、同时多个输出、没有输入引脚,程序要输出错误提示;
新增子电路展开逻辑,读取 C 开头的子电路块,给内部所有引脚加专属前缀,防止不同子电路引脚重名冲突。
2. 代码工具分析

三份作业里代码量最大、类最多的一份,新增了很多独立工具方法:检查连线格式、判断引脚是输入还是输出、自动创建器件、拆分引脚名字。
优点是功能拆分到位,每个小方法只干一件事;缺点是子电路前缀处理、文本多层解析的代码嵌套很深,初次报错很难定位问题。
3. 最终迭代感悟
这次完整走完一套软件升级流程:兼容旧功能、新增复杂业务、增加报错检测、处理命名冲突。
哈希容器的优势完全体现出来,通过名字快速查找引脚,解决了前两次链表遍历卡顿的问题。
语法校验模块是全新挑战,需要提前规定好语法规则,再逐个判断文本是否符合规范,锻炼了文字处理、逻辑判断能力。
二、踩坑心得
坑 1:作业 4 信号循环传递死循环,输出电平反复错乱
现象:简单与门电路输入 1、1,偶尔输出 0,多次循环运行电平来回跳变。
原因:采用 do while 无限循环遍历所有门,门计算完成更新下游引脚后,会重复触发上游门二次计算,形成循环更新。
解决办法:作业 5 改用队列,只有输入齐全、输出为空的门才加入队列,计算完成不再重复入队,彻底解决循环更新问题。
心得:单纯暴力循环处理状态变化逻辑有漏洞,有序队列调度更适合模拟信号单向传递。
坑 2:所有版本查找引脚用链表遍历,样例一多运行卡顿
现象:测试大型电路样例时,程序运行很久才出结果。
代码原因:findPin 方法从头到尾遍历 LinkedList,电路里引脚超过 50 个后,每次查找都要遍历全部。
修复:作业 6 更换 HashMap<String,InterFace>,引脚名字作为 key,直接一步获取,查找效率大幅提升。
心得:需要频繁按名称查找的数据,不能只用链表,哈希映射是更合适的容器。
坑 3:作业 4 父类写死单输出,作业 5 重构大量代码
现象:想写译码器(多个输出引脚),原来的 Door 类只有一个 output 引脚,完全无法复用。
踩坑数据:重构父类、修改所有子类构造方法,花费近两小时,期间多次改错引脚创建逻辑,测试报错十几次。
收获:写基础框架时不能只满足当前需求,要预留拓展空间,遵循通用模板设计,否则后期升级成本极高。
坑 4:作业 6 子电路引脚重名,多个子电路互相干扰
现象:两个子电路内部都有引脚 1,解析后电平互相覆盖,输出全部错误。
根源:子电路内部引脚没有区分标识,全局共用一套名字。
解决方案:读取子电路时,给内部所有引脚名称加上 C1-、C2 - 专属前缀,全局不会重名。
心得:多模块嵌套场景下,必须设计命名隔离规则,防止标识符冲突。
坑 5:连线文本分割、拆分字符串索引越界报错
三次作业都频繁出现,输入 INPUT 或者连线文字格式不标准时,split 分割数组长度不足,直接数组越界崩溃。
比如输入 “S001-1” 正常拆分两段,少写数字、少横杠就会报错。
作业 6 修复方案:新增语法检测函数,提前判断拆分后数组长度,格式错误直接打印提示,不再崩溃退出。
心得:读取外部文本数据时,一定要提前做合法性判断,不能默认输入格式全部标准。
坑 6:作业 6 连线顺序写反、多个输出引脚,无报错直接逻辑错乱
最初没有语法检测,连线把输入写在最前面、多个输出放一条连线里,程序不会提示错误,只会算出完全不对的电平,很难排查。
新增 checkConnection 检测方法后,自动判断:一条连线只能第一个是输出,只能有一个输出引脚,否则主动输出 ERROR 提示。
心得:程序不能只处理正确输入,还要主动拦截非法输入,给出明确错误提示,降低调试难度。
三、三份代码可持续改进建议
- 统一优化:抽离重复工具方法,减少代码冗余
三次作业里,拆分引脚名称、提取器件编号、判断门类型的代码重复写了很多次,可以单独新建一个工具类,把所有通用静态方法放进去,Main 类不用堆砌大量工具代码,结构更清爽。 - 容器统一规范化
作业 4、5 混用 LinkedList,作业 6 才换成 HashMap,后续写同类程序可以直接规划:
需要按名称快速查找:HashMap;
只按顺序遍历、少量增删:LinkedList;
存储不重复标识:HashSet;
提前规划容器类型,不用后期大规模修改存储结构。 - 引脚信号传递逻辑解耦
现在 InterFace 的 setSign 方法里直接循环修改下游引脚,耦合太强。可以把信号下发逻辑单独抽出来,统一在主程序拓扑阶段批量更新,引脚类只负责保存自身数据,不主动修改其他对象,符合单一职责。 - 文本解析模块单独拆分
现在读取 INPUT、子电路块、连线的代码全部写在 Main 里,可以单独创建解析工具类,把读取、分割、预处理文本的代码全部移出主类,主程序只负责调度整体流程,可读性更强。 - 增加日志打印,方便调试
目前程序只有最终输出,中间引脚电平、门计算过程看不到。可以新增简易打印日志方法,调试时打印每个门计算前后的输入输出电平,不用反复修改代码加输出语句,快速定位计算错误。 - 子电路功能拆分独立类
当前子电路解析、展开代码全部堆在 Main 中,后续如果再加多层嵌套子电路,代码会难以维护。可以新建 SubCircuitManager 管理类,专门处理子电路读取、前缀处理、展开逻辑,和主程序解耦。
四、整体阶段性总结
这段学习学到的内容
面向对象基础落地实践:彻底弄懂抽象类、继承的实际用处,学会分层设计程序,把相似功能封装成父类,减少重复代码;掌握类、对象、属性、方法的基础设计思路,能独立画出简易类图梳理代码结构。
Java 容器灵活使用:熟悉链表、队列、哈希表、哈希集合各自适用场景,能根据数据查询、存储需求选择合适容器,理解不同容器性能差距。
字符串文本处理:多次练习分割、截取、匹配文本,学会处理不规则输入,提前做格式校验防止程序崩溃。
迭代开发思维:完整经历三次版本升级,学会在旧代码基础上新增功能、修复缺陷,兼容原有业务,理解 “程序不是一次写完,是持续迭代完善”。
逻辑问题排查能力:大量调试报错后,学会通过打印中间数据、拆分模块、分步测试定位代码 bug,不再出现报错就无从下手。
自身不足,后续需要加强学习
前期架构设计能力薄弱,写代码只盯着当前作业要求,很少考虑后续拓展,经常需要大规模重构,后续写程序先画类图梳理结构再动手编码。
代码拆分意识不足,习惯把大量逻辑堆在主函数,不会主动拆分独立工具类、工具方法,代码臃肿难维护,之后刻意练习单一职责编码。
底层运算熟练度不够,位运算、多路地址译码逻辑写起来耗时久,需要多练习数字逻辑相关编程题目。
异常处理思路欠缺,之前不会主动预判非法输入,只会处理标准样例,后续写输入解析模块优先增加格式校验、错误提示。
调试手段单一,只会打印输出排查问题,后续学习日志打印、分步断点调试,提升排错效率。
整体收获感悟
三次递进式作业对大一刚接触面向对象的我提升很大,最开始只会写单类简单程序,现在能独立完成多类分层、带嵌套语法、自带报错检测的完整小程序。
踩过大量重复、设计、逻辑坑之后,明白编程不只是把功能跑通,更重要的是代码结构清晰、方便后续修改、容错性强。这次完整复盘三份迭代代码,梳理清楚每一次的缺陷和改进方向,也能作为之后学习、写课程设计的参考,积累完整项目迭代经验。

浙公网安备 33010602011771号