作业集4~6 总结性博客:数字电路仿真器的迭代开发
一、前言
相比于上一次作业,此次作业由于专业术语较多,所需要的专业知识更多,做起来比较吃力。但此三次作业延续了上一次的逐步深入,将第四次作业写出来之后,后面写起来更加得心应手。此次作业围绕围绕“数字电路仿真器”这一主题进行了三轮迭代开发。三次作业逐步深入,从基础组合逻辑仿真,到多输出元件与信号处理,再到支持子电路嵌套的层次化设计,覆盖了面向对象编程、解析器设计、信号传播算法等多个知识点。
| 作业集 | 主要知识点 | 类 |
| 7-1 NCHUD-数字电路模拟程序-1 | 面向对象编程(类、对象、方法封装) 正则表达式( Pattern、Matcher)集合框架( Map、List,含 LinkedHashMap、HashMap、ArrayList)迭代算法与标志位控制( while(changed) 信号传播)多路分支与逻辑运算( switch-case)静态方法与静态初始化块 Scanner 标准输入读取Comparator 与 Lambda 表达式排序字符串解析( split、substring) |
6个核心类 |
| 7-1 NCHUD-数字电路模拟程序-2 |
与作业4基本一致,并增加: |
6个核心类 |
| 7-1 NCHUD-数字电路模拟程序-4 |
抽象类、继承与多态(Component 抽象类及其子类) |
12个类/接口 |
二、设计与分析
2-1 NCHUD-数字电路模拟程序-1
核心类:
- Gate
- Circuit
- Simulator
- Parser
- Reporter
- Main 程序入口
类间关系:

复杂度分析:

心得:
作业 4 的核心实现思路为构建计算图模型,并通过迭代更新的方式完成组合逻辑求解。仿真器中基于while(changed)的迭代循环能够适配任意器件互连拓扑,但存在两处明显缺陷:一是执行效率较低;二是不具备组合环路检测能力,电路中若存在组合环路,会引发死循环或输出逻辑错误。
2-2 NCHUD-数字电路模拟程序-2
核心类:(与作业四基本相同,但在Reporter 和 Gate 的部分方法中增强了对多输出元件(译码器、数据分配器)的格式化支持。)
- Gate (
compute()中M型译码器使能条件修正(S1=1,S2=0,S3=0)) - Circuit
- Simulator
- Parser
- Reporter (1)prepareOutput() 中针对 M 型译码器输出格式化为 名字:索引(输出低电平有效通道的索引);
(2)针对 F 型数据分配器输出格式化为 名字:--0- 形式的信号串(有效位为实际值,无效位为 -)
- Main 程序入口
类间关系:

复杂度分析:

心得:
该实现虽可保证功能运行无误,但 Gate 类职责划分不合理,承载了大量引脚初始化相关逻辑。各类器件的控制引脚数量、输入输出引脚排布均在initPins()方法内通过 switch 分支硬编码实现,后续新增器件类型时,会持续膨胀该方法,造成类结构臃肿、维护成本上升。
优化方案可引入工厂模式或策略模式解耦引脚初始化逻辑;但结合作业 5 工程体量,当前简化实现方案具备可行性,可予以保留。
失误:
在书写过程中,由于某些格式问题,在基础元件测验中有问题。
2-3 NCHUD-数字电路模拟程序-4
核心类:
- Pin
- Component(抽象类)
- AndGate / OrGate / NotGate / XorGate / XnorGate 具体组合逻辑门,实现 computeOutput()
- SubCircuitComponent
- Circuit
- Connection
- Parser
- CircuitException
- Main 程序入口,读取输入,调用 Parser 构建顶层电路并仿真输出
类间关系:

复杂度分析:

心得:
层次化架构设计赋予电路模型极高的拓展与复用灵活性,但同时提升了调试排查成本。跨层级的信号互连映射需依托SubCircuitComponent.evaluate()手动完成,机制等效于硬件描述语言中的模块端口绑定逻辑。
本次课程实践充分体现,规范合理的数据结构、分层清晰的解析执行流程,是搭建复杂电路仿真系统的核心基础。
失误:
此次作业中,难度增加,沿用之前的类时,会有些答案格式错误,或者输出案例错误。
三、问题
作业四:搭建门级仿真器
1.Gate.compute() 方法体积膨胀,难以扩展
所有门电路的逻辑均集中在一个 compute() 方法内,通过大型 switch 语句区分类型。当需要添加新器件时,必须直接修改该方法,导致其规模不断增大,且容易在改动时引入错误。这种设计不符合“对扩展开放、对修改封闭”的原则。可为每种门创建独立子类,各自封装计算逻辑。
2.迭代仿真算法可能因组合环路而无法终止
Simulator 使用 while (changed) 循环反复计算所有门,直至没有输出发生变化。这种方法在遇到组合环路(如两个非门首尾相连)时,信号会持续翻转,循环永远无法退出,导致程序卡死。解决思路包括设置最大迭代次数上限,或采用拓扑排序一次性计算,既能提高效率,也能检测环路。
3. 输入引脚多驱动冲突时缺乏报错机制
记录驱动关系时使用了 putIfAbsent,该操作在输入引脚已被驱动时会静默忽略后续连接。如果同一输入不慎被多个输出驱动,冲突不会被提示,仿真结果却可能错误,增加了调试难度。
作业五:增强多输出元件支持
1. Reporter 中输出格式逻辑混杂,可读性差
译码器需要输出 名字:索引,数据分配器需要输出 名字:--0- 形式的信号串,普通门则是 引脚:值。这些格式化逻辑全部集中在 Reporter 类中,导致方法内出现大量 if (type == 'M') 等类型判断,结构混乱。可改为每种元件添加自描述的输出格式化方法,由元件自身负责生成字符串,Reporter 仅负责排序与调用。
2.多输出门中无效信号 -1 的传播不完整
单输出门在输入未知时只需返回一个 -1,但多输出门(如 F 型数据分配器)在控制信号无效时,必须将所有输出都置为 -1。初期实现仅将出错通道置为无效,其余输出保留了默认值,导致仿真结果部分正确,不易察觉。在设计 invalidM() 和 invalidF() 方法时,需确保返回列表长度与输出引脚总数一致,且每个元素均为 -1。
作业六:实现层次化子电路仿真
1. 子电路定义顺序与引用顺序不一致
解析输入时,可能在子电路 C1 的定义内部引用了尚未定义的子电路 C2(因为 C2 的定义写在后面),导致解析失败。解决方法是采用两遍解析:第一遍扫描所有 C1: … endc 块,创建子电路对象并注册;第二遍再解析各子电路内部的连接关系。这样无论定义顺序如何,引用时对象均已存在。
2. 子电路输入输出引脚方向混淆
SubCircuitComponent 的外部引脚有输入、输出之分,内部子电路的引脚同样有 INPUT/OUTPUT 方向。在连接时容易将方向弄反,例如将外部输入误判为可驱动其他引脚的源,导致信号传递错误。通过在纸上绘制接口对应关系可帮助理清思路。规范做法是为 Pin 设置 INPUT/OUTPUT 枚举,连接时强制检查源引脚必须是 OUTPUT,目标引脚必须是 INPUT,否则抛出异常。
3.异常信息过于简略,不利于调试
当连接格式或元件定义出现错误时,程序仅抛出 CircuitException("Unexpected line: ..."),没有提供出错行号或具体原因。改进方法是在异常信息中附加上下文,如当前行号、冲突引脚名称或元件类型,从而大幅提升问题定位效率。
四、总结
通过作业四至作业六的迭代开发,我从零构建了一个支持层次化子电路的数字电路仿真器。三次作业难度递进、功能逐步扩展,从平坦门级仿真到多输出元件增强,最终实现支持子电路定义的层次化系统,完整覆盖了面向对象设计、解析器构建与仿真算法等核心编程能力。
- 作业四建立了基本的门级仿真模型,采用迭代算法求解组合逻辑,涉及正则解析、集合框架与基本类结构设计。作业五在作业四基础上增强了对译码器、数据分配器等多输出元件的支持,重点修正了使能条件与输出格式化逻辑。作业六引入子电路定义与实例化,通过递归下降解析和组合模式实现了树形层次结构的信号传播。整个过程中运用的核心知识点包括:抽象类、继承与多态,正则表达式匹配,集合框架(Map、List)的使用,以及递归下降解析与组合模式的应用。
- 开发过程中遇到的主要问题包括:单一方法体积膨胀导致扩展困难,引脚编号硬编码偏移量降低了灵活性,迭代仿真可能在组合环路中陷入死循环,多驱动冲突被静默忽略导致调试困难,使能条件写反造成译码器始终不工作,多输出门无效信号传播不完整,子电路定义顺序与引用顺序不一致,输入输出引脚方向混淆,外部输入未同步至子电路内部,以及异常信息过于简略。针对这些问题的改进方向有:为每种门建立独立子类遵循开闭原则,采用拓扑排序替代迭代循环以提升效率并检测环路,引入工厂模式或策略模式增强可扩展性,在冲突发生时抛出明确异常,以及完善错误信息包含行号和具体原因。
通过这三次作业的实践,不仅加深了对面向对象原则中封装、继承与多态的理解,也切身体会到解析器设计、信号传播算法和层次化建模在实际工程中的重要性与复杂度。

浙公网安备 33010602011771号