总结4-6
一.前言
本次作业 4、5、6 为数字逻辑电路仿真系列递进开发任务,三次作业围绕 “单层基础电路,面向对象设计模式重构,支持子电路分层嵌套” 逐步拓展,完整覆盖面向对象封装、继承多态、观察者设计模式、子电路模块化、拓扑排序信号传播、输入语法校验、全局引脚命名映射等核心知识点,下面从设计分析、采坑心得、改进建议、阶段总结四个维度对三次作业做整体梳理。
二.设计与分析
1.作业 4
核心需求:
数字电路是一种处理离散信号的电子电路。与处理连续变化信号(如声音、温度)的模拟电路不同,数字电路只识别和运算两种基本状态:高电平(通常表示为“1”) 和 低电平(通常表示为“0”)。这正好与二进制数制系统相对应,使得数字电路成为所有计算机和数字系统的物理实现基础。
类图:

这次作业只有一个核心实体类:Gate;
Gate 为唯一业务实体,封装type门类型、inCount输入引脚数量、id编号、fullName全称、inputPin引脚映射哈希表、output输出值六大私有属性,构造方法统一初始化全部成员。核心方法calculate()依靠 switch 分支区分五类门电路运算规则,读取引脚 Map 内信号完成逻辑计算。
还有Main主程序;
Main 依靠三个静态全局容器gatePool、globalInput、connections存储全部电路数据,依靠 parseGate 正则解析门名称、createGateFromPin 自动创建门实例,主循环重复遍历所有连线、刷新引脚信号、调用 calculate 计算,直至电路输出不再变化,完成收敛仿真。
设计心得:
最开始只想着代码能算出结果就行,设计完全偷懒。所有门电路运算挤在同一个方法,靠全局变量存全部数据,结构很乱。写完才发现扩展性极差,加新门、优化仿真逻辑都要大改代码,暴力循环还容易死循环,只完成了最基础的封装,完全没考虑代码复用和效率。
2.作业 5
核心需求:
数字电路是一种处理离散信号的电子电路。与处理连续变化信号(如声音、温度)的模拟电路不同,数字电路只识别和运算两种基本状态:高电平(通常表示为“1”) 和 低电平(通常表示为“0”)。
这正好与二进制数制系统相对应,使得数字电路成为所有计算机和数字系统的物理实现基础。
类图:

类与接口:
1.顶层接口层
Subject(被观察者):定义 attach、detach、notifyAllObs 订阅通知方法,Pin引脚类实现该接口;
Observer(观察者):定义 update () 回调方法,抽象父类Chip实现该接口。
2.状态枚举层
enum Level:定义 LOW (0)、HIGH (1)、INVALID (无效) 三种电平状态,统一规范信号状态,替代作业 4 裸数字标识。
3.引脚实体类 Pin
实现 Subject 接口,持有所属芯片、引脚编号、电平状态、观察者订阅列表;引脚电平变更时自动通知所有订阅芯片执行计算。
4.抽象父类 Chip
实现 Observer 接口,封装芯片名称、引脚集合;提供抽象方法calc()供子类重写;重写 update (),收到引脚变更通知自动调用 calc () 完成运算,实现事件驱动。
5.芯片子类(继承 Chip,重写 calc () 多态)
AndGate、OrGate、NotGate、XorGate、XnorGate、TriState 三态门、Decoder 译码器、Mux 多路选择器、Demux 多路分配器,每类芯片独立实现专属运算逻辑,新增芯片仅需新增子类,不修改原有代码,符合开闭原则。
6.Main 主调度类
提供全局芯片容器、输入源引脚存储、自定义排序规则、文本解析、信号联动调度逻辑。
设计心得:
这次用抽象类、观察者模式重构,才算懂了解耦。拆分接口、父类和各类芯片子类,引脚变化自动触发计算,淘汰了作业 4 低效的全局遍历。多态拆分运算逻辑,不用写巨型 switch,新增芯片只需新建子类。但代码只能单层电路运行,没有错误校验,输入格式出错程序直接崩溃,没法实现电路模块化复用。
并且此次作业有很多测试点没对:

怎么也改不对。遇到的困难我会在下一个板块讲。
3.作业 6
核心需求:
数字电路是一种处理离散信号的电子电路。与处理连续变化信号(如声音、温度)的模拟电路不同,数字电路只识别和运算两种基本状态:高电平(通常表示为“1”) 和 低电平(通常表示为“0”)。这正好与二进制数制系统相对应,使得数字电路成为所有计算机和数字系统的物理实现基础。本题目在“NCHUD-数字电路模拟程序-1”的基础上迭代,增加的功能参见题目中的标注。
类图:

类与接口:
1.SubCircuit 子电路类
存储子电路编号、局部输入 / 输出引脚名、局部引脚与全局引脚双向映射 MapinToGlobal、outToGlobal,实现子电路命名空间隔离,解决多层嵌套引脚重名冲突。
2.Component 组件门类
封装门全称、类型、组件编号、所属子电路编号、输入引脚数量、输入引脚列表、输出引脚,替代作业 4、5 分散的 Gate/Chip 实体,统一门电路数据模型,兼容所有基础逻辑门。
3.ConnectionInfo 连线信息类
存储原始连线文本、所属子电路上下文、引脚列表,记录每条连线的来源层级,用于全局引脚名称转换、语法错误定位。
4.Main 调度主类
提供子电路容器、顶层输入存储、全局连线集合、拓扑排序队列、引脚值映射表,配套toGlobalName命名转换、parseComponent门解析、calcGateOutput门运算、语法校验全套工具方法。
设计心得:
引入子电路类实现分层嵌套,靠引脚全局映射解决多层命名冲突,还用拓扑队列优化信号传播。新增完整语法校验,能定位连线冲突、顺序错误等问题,鲁棒性提升很多。局限是所有类都放在单个文件,没做分包分层,也没兼容作业 5 的译码器、三态门,异常处理简陋,仍有大量优化空间。
这次作业也有很多测试点没过,最后只让非零返回变成答案错误:

三.踩坑心得
第四次作业踩坑记录:
踩坑 1:全局循环收敛死循环
现象:部分多输入 AND 门电路运行后程序无限循环,无法跳出 do-while 收敛循环。
根因:源码中仅对比门输出是否变化,未考虑输入引脚反复刷新的场景;当两个门互相作为对方输入时,输入引脚持续变更,output 每次循环都发生变化,changed恒为 true,死循环。
解决:增加最大迭代次数阈值限制,超过 200 次直接判定电路存在环路。
踩坑 2:引脚空指针异常
现象:解析连线时 src 包含门引脚,gatePool 未提前创建对应 Gate 实例,调用 g.inputPin.put 直接抛出空指针。
根因:原代码仅在连线目标处创建门,源引脚对应门未提前初始化;parseGate 返回实例未存入 gatePool。
解决:新增createGateFromPin工具方法,解析任意引脚字符串时自动创建对应门并存入全局容器。
第五次作业踩坑记录:
踩坑 1:观察者订阅重复,calc () 重复多次执行
现象:单个引脚电平变更后,对应芯片 calc () 方法执行 3~4 次,输出电平错乱。
根因:AndGate 构造方法循环 attach 引脚至芯片,未判断 obsList 是否已包含当前观察者,同一芯片多次订阅同一引脚,notifyAllObs 时多次回调 update。
解决:Pin.attach 方法增加if (!obsList.contains(obs))去重判断,修复后重复计算现象消失。
踩坑 2:向下转型类型转换异常
现象:调用 allValid () 校验芯片输入时,Chip 强转为 AndGate、OrGate 抛出 ClassCastException。
根因:未通过芯片类型字符提前判断芯片种类,直接强制转型;译码器、多路选择器芯片执行对应 AndGate 转型代码触发异常。
解决:switch 匹配芯片首字符 type,仅对应类型执行向下转型校验逻辑,其余芯片跳过对应分支。
第六次作业踩坑记录:
踩坑 1:子电路多层嵌套引脚重名,信号覆盖冲突
现象:顶层输入 IN-1 与子电路 C1 内局部输入 1 同名,解析后引脚值被互相覆盖,门电路输出全部错误。
根因:未做全局命名隔离,子电路局部引脚直接存入全局 pinValueMap,命名冲突覆盖原有电平。
解决:实现toGlobalName转换函数,所有子电路内部引脚统一拼接前缀 C {id}-,生成全局唯一标识存入容器,隔离局部命名空间。
踩坑 2:连线源 / 目标顺序校验逻辑颠倒
现象:输入连线[A1-0 IN1](输出引脚在前、输入在后),程序未报错,电路计算结果完全颠倒。
修复:新增判断if (!isSourcePin.get(0)),源引脚不在首位直接输出 ERROR 终止程序,严格规范连线语法。
踩坑 3:同一引脚被多条连线驱动,信号冲突无检测
现象:两个连线同时向同一个门输入引脚赋值,电平随机覆盖,无任何提示。
根因:缺少 drivenPins 集合记录已被驱动引脚,重复驱动无校验;
解决:遍历所有连线目标引脚存入 HashSet,存入前判断是否已存在,重复驱动直接打印冲突 ERROR。
踩坑 4:拓扑队列信号传递遗漏子电路全局输出映射
现象:子电路内部门输出电平正常,但顶层全局引脚无信号,输出为空。
根因:BFS 队列传递局部子电路输出引脚时,未同步更新对应的全局引脚映射localToGlobalOut;
解决:destPin 匹配局部输出引脚时,同步取出对应全局引脚,写入 pinValueMap 并加入队列向下传递信号。
四.改进建议
(一)作业 4→作业 5 迭代改进建议
1.彻底删除全局静态容器,替换为封装调度类,消除全局变量高耦合;
2.用抽象类 + 多态拆分 calculate 巨型 switch 分支,新增门电路无需修改原有代码;
3.引入观察者事件驱动替代全局循环收敛仿真,减少冗余迭代运算;
下面是我的流程图:

(二)作业 5→作业 6 迭代改进建议
1.新增 SubCircuit 子电路模块化封装,实现电路块复用、多层嵌套;
2.增加全局 - 局部引脚双向命名映射,隔离子电路命名空间,解决引脚重名冲突;
流程图:

(三)作业 6 长期可持续拓展改进
1.新增环路检测算法,识别互相耦合的环路电路,输出环路告警;
2.增加单元测试用例,针对单层电路、嵌套子电路、错误输入、环路电路编写测试用例,自动化验证仿真结果;
流程图:

五.综合总结
-
本阶段三次作业学到的核心知识
基础面向对象编程体系:类封装、访问控制、构造重载、容器 HashMap/ArrayList 数据结构使用;
进阶面向对象设计:抽象类、继承、方法重写、多态、向下转型、Comparable 自定义排序;
设计模式落地:观察者 Observer-Subject 模式,实现事件驱动自动联动逻辑;
软件工程模块化思维:子电路分层封装、命名空间隔离、全局局部映射解耦;
算法与仿真逻辑:正则文本解析、BFS 拓扑排序、循环收敛迭代、图邻接表存储电路连线;
标准化开发工具使用:SourceMonitor 量化代码复杂度、PowerDesigner 绘制 UML 类图,通过数据量化评估代码质量;
工程容错思维:输入语法校验、冲突检测、边界场景处理、错误信息友好输出。
从思维转变层面,完整完成了从 “过程式写代码实现功能” 到 “面向对象分层、模块化、可拓展工程化开发” 的转变,不再只关注程序能否输出结果,而是同步思考代码复用、拓展性、耦合度、运行效率、异常容错等软件工程核心指标。 -
仍存在不足、需要进一步学习研究的内容
设计模式掌握局限:仅熟练掌握观察者模式,工厂模式、单例模式、组合模式(适配子电路树形嵌套)未实践,后续需要系统学习 23 种常用设计模式并结合电路项目落地;
图形化 GUI 开发薄弱:仅完成控制台仿真,JavaFX 界面绑定、数据可视化、画布绘制电路连线等内容练习不足,需要补充 GUI 项目实操;
单元测试、自动化测试体系缺失:三次作业均为手动输入测试用例,未学习 JUnit 单元测试框架,无法自动化批量验证电路仿真结果;
算法优化深度不足:拓扑排序仅实现基础 BFS 版本,环路检测、电路依赖深度优化、大规模电路并行运算等进阶算法有待研究; -
整体学习感悟
三次作业循序渐进、层层递进,每一次作业都精准暴露上一版代码的设计缺陷,倒逼我优化架构、更新编程思维。作业 4 只追求功能可用,作业 5 学习用面向对象设计模式解耦,作业 6 建立模块化分层工程思维,完整体验了小型软件从原型、重构、工程化完善的完整流程。
通过 SourceMonitor 量化代码、PowerDesigner 绘制 UML 类图,我意识到编码不能只关注运行结果,代码复杂度、耦合度、拓展性、可读性同等重要。后续学习会更加注重前期架构设计,提前梳理分层、实体关系、边界异常场景,减少后期大量重构调试的成本,持续向规范化、工程化软件开发思维进阶。

浙公网安备 33010602011771号