数字电路模拟程序
一、前言
第四次作业(数字电路模拟程序 - 1)
基础类划分:程序按照单一职责原则拆分为抽象基类、具体元件类、信号实体类、核心处理器类与主程序入口类共 9 个类,每个类仅负责自身领域内的逻辑,避免单类职责膨胀。其中Men作为所有门电路的抽象父类,封装通用属性与方法;五个具体门类分别实现自身的逻辑运算;Signal封装引脚与电平的对应关系;CircuitHandler承担输入解析、信号调度、计算控制、结果输出的全流程调度;Main仅作为程序入口,不包含任何业务逻辑。
继承与多态应用:通过抽象基类定义统一的计算接口与属性规范,所有具体门电路都继承自该基类并重写计算方法。核心调度时仅面向抽象基类编程,无需感知具体元件类型,利用多态特性实现统一调度,新增元件时无需修改原有调度逻辑,初步体现开闭原则。
迭代信号传播算法:针对多级串联电路的依赖问题,采用循环迭代的计算方式,每一轮遍历所有元件更新输出并同步信号,直到本轮无任何信号变化时终止,确保所有可计算的元件都能得到稳定正确的输出结果。
输出规则实现:严格遵循题目指定的输出顺序与格式要求,对元件进行多级排序,同时过滤无效输出元件,保证输出结果的规范性与正确性。
难度:中等。核心难点在于理解继承多态在业务场景中的落地方式,掌握迭代传播算法解决多级电路的依赖计算问题,同时准确实现五种逻辑门的运算规则。
第五次作业(数字电路模拟程序 - 2)
继承体系扩展:在原有Men继承链上新增四个元件子类,完全复用基类的引脚管理、信号读写、状态标记等通用逻辑,仅扩展自身的引脚定义与计算规则,严格遵循开闭原则,在不修改核心调度代码的前提下完成功能扩展。
引脚分类设计:将元件引脚划分为控制引脚、输入引脚、输出引脚三类,不同引脚遵循不同的连接规则与计算逻辑。控制引脚用于决定元件工作状态与通道选择,输入引脚用于接收待运算信号,输出引脚用于输出运算结果,三类引脚编号独立划分,职责清晰。
无效状态统一机制:引入-1作为无效输出的统一标记值,三态门高阻态、译码器控制无效状态、数据分配器未选中通道均标记为无效值。输出时自动过滤完全无效的元件,信号同步时自动移除无效引脚的信号,保证整个系统状态的一致性。
二进制编码映射逻辑:译码器、数据选择器、数据分配器三类元件均涉及控制端二进制编码到通道编号的映射,需要准确处理引脚编号与位权的对应关系,实现 n 位控制端对应 2ⁿ个通道的通用换算逻辑。
难度:偏难。核心难点在于准确理解各类元件的引脚规范与运算逻辑,正确实现二进制编码的高低位对应关系,同时设计统一的无效状态管理机制,保证多输出元件的信号同步准确无误。
第六次作业(数字电路模拟程序 - 4)
组合设计模式应用:将基础逻辑门视为组合模式中的叶子节点,将子电路视为组合节点,二者对外具备一致的端口交互特性。子电路内部封装独立的元件集合与连接关系,对外仅暴露输入输出端口,隐藏内部实现细节,既实现了电路复用,又保证了封装性。
子电路命名隔离机制:为每个子电路分配独立的编号,子电路内部的所有元件自动添加编号前缀,与主电路及其他子电路的命名空间完全隔离,避免不同子电路间元件编号相同导致的冲突。子电路仅通过端口与外部交互,内部元件对外部不可见。
分级异常检测体系:按照题目指定的优先级,依次检测 “包含多个输入源”“无输入源”“无接收端”“输入输出顺序错误”“输入信号冲突” 五类异常,仅输出首个出现的最高优先级异常,保证错误提示的唯一性与规范性。异常检测在连接解析阶段前置执行,发现错误后终止后续处理,避免非法数据进入计算流程。
全局导线连接模型:引入wireMap维护所有导线的连接关系,driverMap记录每个接收引脚的驱动源,构建全局的信号连接网络。每次迭代计算时,先同步所有导线上的信号,再计算元件输出,解决跨子电路端口、多级嵌套电路的信号自动传递问题,确保信号在整个电路网络中正确传播。
二、设计与分析
第四次作业
Men抽象基类承担了所有门电路的共性封装:它包含元件名称、类型、编号等基础属性,包含引脚源数组用于记录每个输入引脚的信号来源,包含输出值属性存储计算结果。同时,它定义了compute抽象方法作为所有元件的统一计算接口,还提供了设置引脚源、获取输出值等通用方法。这种设计将所有元件的共性完全抽取到父类中,子类仅需关注自身的差异化逻辑,大幅减少了重复代码。A、O、N、X、Y都继承自Men基类,仅重写compute方法实现各自的逻辑运算。其中与门遍历所有输入,存在 0 则输出 0;或门遍历所有输入,存在 1 则输出 1;非门对单输入取反;异或门在两输入不同时输出 1;同或门在两输入相同时输出 1。每个类的代码都非常精简,职责单一,仅负责自身的运算规则,符合单一职责原则。CircuitHandler作为核心处理器,是整个程序的调度中心。它内部维护信号列表与元件列表,提供信号读写的基础方法,同时承担输入解析、连接绑定、迭代计算、排序输出四项核心职责。在连接解析时,程序会自动根据引脚名称创建对应的元件实例,自动绑定输入引脚的信号源;在迭代计算时,采用 “信号变化驱动” 的循环策略,每一轮遍历所有元件,对输入完备的元件执行计算并更新输出信号,直到整轮遍历没有任何信号变化为止,确保计算结果收敛。源码分析:


类图:

类间关系详解
1.泛化(继承)关系:Men抽象基类与A、O、N、X、Y五个具体门类是典型的泛化关系。子类继承父类的所有非私有属性与方法,并重写父类的抽象方法compute,符合里氏替换原则,所有出现父类的地方都可以用子类替换,这也是核心调度逻辑可以统一面向Men编程的基础。
2.聚合关系:CircuitHandler与Men、CircuitHandler与Signal均为聚合关系。CircuitHandler作为整体,持有List<Men>门元件集合与List<Signal>信号集合,但门元件与信号都可以独立于处理器存在,二者的生命周期没有强绑定,处理器只是对元件与信号进行统一管理,属于 “弱拥有” 的聚合关系。
3.依赖关系:Main类与CircuitHandler是依赖关系,Main仅在main方法中临时创建CircuitHandler实例并调用其方法,属于 “使用” 关系,没有长期持有成员引用;同时Men类的计算逻辑依赖CircuitHandler提供的信号读写能力,计算过程中需要通过处理器获取输入信号的值,属于方法级别的依赖关系。
4.关联关系:Men内部通过引脚源数组与信号形成间接关联,每个输入引脚都记录了对应的信号源名称,通过处理器可以查找到对应的信号值,是结构化的引用关联。
Men与五个子类是典型的泛化(继承)关系;CircuitHandler与Men、Signal是聚合关系,处理器聚合管理所有元件与信号实例;Main与CircuitHandler是依赖关系,主程序仅依赖处理器启动整个流程。整体结构层次清晰,耦合度低,为后续版本的功能扩展打下了良好的架构基础。第五次作业
Men基类进行了升级:将原本单值的输出属性替换为Map<Integer, Integer>结构的输出映射表,支持存储多个引脚的输出值;同时新增setOutputInvalid方法,统一将指定引脚标记为-1无效值。这种升级完全向后兼容,原有单输出元件依然只使用 0 号引脚,不会受到任何影响,保证了版本迭代的兼容性。三态门S设置 0 号引脚为控制端、1 号引脚为输入端、2 号引脚为输出端。当控制端为高电平时,输出跟随输入;当控制端为低电平时,输出标记为无效高阻态,符合三态门的总线特性。
译码器M设置前 3 个引脚为控制端,后续为输入引脚,最后为输出引脚。只有当控制端满足 “1、0、0” 的使能条件时,才根据输入二进制编码将对应输出引脚置 0、其余置 1;控制无效时所有输出引脚标记为无效。
数据选择器Z前半段为控制引脚,中间为多路输入引脚,最后为单输出引脚。根据控制端的二进制编码,从多路输入中选择对应通道的信号输出,实现多路选通的功能。
数据分配器F前半段为控制引脚,中间为单路输入引脚,最后为多路输出引脚。根据控制端编码将输入信号送到对应输出通道,其余未选中通道全部标记为无效。
CircuitHandler的迭代算法进行了适配优化:每次元件计算完成后,遍历其所有输出引脚,有效信号更新到信号列表,无效信号主动从信号列表中移除。这种机制保证了信号状态的准确性,避免无效信号残留导致下游元件误计算。同时,元件创建逻辑仅新增了四个分支,迭代计算的核心循环完全不变,体现了架构的可扩展性。源码分析:


类图:

CircuitHandler的迭代算法进行了适配优化:每次元件计算完成后,遍历其所有输出引脚,有效信号更新到信号列表,无效信号主动从信号列表中移除。这种机制保证了信号状态的准确性,避免无效信号残留导致下游元件误计算。同时,元件创建逻辑仅新增了四个分支,迭代计算的核心循环完全不变,体现了架构的可扩展性。-填充。输出前先按类型优先级、再按元件编号排序,严格匹配题目输出规范。类间关系详解
1.泛化(继承)关系:新增的S、M、Z、F四个类均继承自Men抽象基类,与原有五个子类平级,共同构成完整的元件继承树。所有子类都可以替换父类在调度逻辑中使用,进一步验证了继承体系的扩展性,开闭原则得到了充分体现。
2.组合关系:Men基类内部的输出映射表outputValues、引脚源数组与元件对象之间是组合关系。这些属性是元件对象的固有组成部分,与元件对象同生共死,元件实例创建时属性随之初始化,元件销毁时属性也随之消失,属于 “强拥有” 的组合关系。
3.聚合关系:CircuitHandler与所有门类依然保持聚合关系。虽然元件种类增加,但处理器只是统一管理所有元件实例,元件的生命周期不与处理器绑定,依然是弱拥有的聚合关系。
4.依赖关系:CircuitHandler的计算逻辑依赖每个元件的compute方法,输出逻辑依赖不同元件的类型判断,均属于依赖关系。处理器通过调用元件的方法完成计算与输出,不需要感知元件内部的具体实现。
第六次作业
SubCircuit类用于描述子电路实体,它包含子电路编号、输入端口集合、输出端口集合三个核心属性,负责定义子电路的对外接口。子电路内部的元件通过命名前缀机制实现隔离:所有内部元件的全局名称都会自动加上C编号-前缀,确保不同子电路间即使元件编号相同,也不会产生命名冲突。这种命名空间隔离的实现方式简单高效,完美解决了模块化复用的命名冲突问题。wireMap以源引脚为键、目标引脚列表为值,记录所有导线的连接关系;driverMap记录每个接收引脚的唯一驱动源,用于冲突检测。每次迭代计算时,先遍历所有导线,将源引脚的信号同步到所有目标引脚,再进行元件计算。这种模型完美解决了跨子电路端口的信号传递问题:子电路输入端口接收外部信号后,通过导线传递给内部元件;内部元件的输出通过导线传递给子电路输出端口,再传递到外部主电路,整个过程自动完成,无需特殊处理。源码分析:


类图:

类间关系详解
1.泛化(继承)关系:重构后的Gate抽象基类与所有基础门类(AndGate、OrGate、NotGate、XorGate、XnorGate)是泛化关系,基础门对应组合模式中的Leaf 叶子节点,没有子节点,自身完成计算逻辑。
2.组合关系:SubCircuit(组合节点)与Gate之间是组合关系。子电路作为整体包含内部的所有门元件,元件的生命周期与子电路强绑定:子电路定义时创建内部元件,子电路销毁时内部元件也随之失效,属于 “强拥有” 的组合关系,完全符合组合模式中 Composite 与 Component 的包含关系。
3.聚合关系:CircuitHandler与SubCircuit、CircuitHandler与Gate均为聚合关系。处理器作为全局管理者,统一持有所有子电路实例与所有门元件实例,但子电路可以独立定义、独立引用,元件也可以存在于不同的子电路上下文中,生命周期不与处理器绑定,属于弱拥有的聚合关系。
4.依赖关系:
Main依赖CircuitHandler启动程序,是入口级依赖;
导线信号同步逻辑依赖wireMap与driverMap两个数据结构,是方法级依赖;
异常检测逻辑依赖引脚类型判断方法,是功能级依赖;
输出排序逻辑依赖元件的类型与编号属性,是数据级依赖。
5.关联关系:SubCircuit通过输入输出端口与外部电路形成关联,端口是子电路与外部交互的唯一接口,内部元件不直接与外部关联,保证了封装性。
三、踩坑心得
第四次作业踩坑
- 单次遍历计算导致后级元件无输出
开发初期,计算逻辑仅对所有元件执行一次遍历计算,运行后发现多级串联的电路中,只有第一级门电路有输出,后面所有门都被判定为输入无效。经过排查发现,第一次遍历时后级门的输入信号还未被前级门计算出来,因此直接跳过了计算,后续也不会再重新计算。
解决方案是改为循环迭代计算,每一轮遍历所有元件更新输出并同步信号,设置changed标记记录本轮是否有信号变化,当一轮遍历没有任何变化时终止循环。修改后所有可计算的元件都能得到正确结果,多级串联电路完全正常。 - 输出排序规则错误
最初实现排序时,仅按照元件编号从小到大排序,运行测试用例后发现输出顺序与预期不符。仔细核对题目要求后发现,输出必须先按元件类型的固定优先级排序,同类型才按编号排序。
解决方案是在比较器中先获取两个元件类型在AONXY字符串中的索引,先按索引排序,索引相同时再按编号排序。调整后输出顺序完全符合题目规范。![image]()
第五次作业踩坑

- 控制端高低位顺序颠倒
开发译码器、数据选择器、分配器三类元件时,默认认为引脚号越小位权越高,导致所有编码计算结果完全错误,大量测试用例不通过。通过手动计算样例的编码结果,对比程序输出反推,发现引脚 0 对应的是最低位 2⁰,引脚 1 对应 2¹,引脚号越低位权越低。
解决方案是修改编码计算逻辑,循环中用controlCode |= (controls.get(i) << i)实现第 i 号引脚对应第 i 位,修正后所有编码结果完全正确。 - 无效状态残留信号问题
最初实现无效状态时,仅标记了元件内部的输出值为 - 1,但没有从全局信号列表中移除对应引脚的信号。导致三态门从导通切换到高阻态后,下游元件依然能读取到之前的有效值,造成计算错误。
解决方案是在每次计算后遍历所有输出引脚,遇到无效值时主动从信号列表中删除对应条目,保证全局信号状态与元件状态一致。修改后无效状态的传递完全正确,下游元件会正确判定为输入无效。 - 三态门引脚编号定义错误
最初想当然地沿用基础门的引脚定义,将 0 号引脚作为输出,导致连接解析时引脚绑定完全错位。重新查阅题目引脚规范后,明确三态门 0 号是控制、1 号是输入、2 号是输出,调整三态类的引脚常量定义后,连接与计算全部正常。
第六次作业踩坑

- 子电路端口解析导致数字格式化异常
运行子电路样例时,程序直接抛出NumberFormatException,报错无法将字符串 “A” 转为整数。排查调用栈发现,在绑定引脚源时,代码默认所有带-的引脚都是 “元件名 - 引脚号” 格式,直接拆分后解析数字,但子电路端口C1-A的后缀是字母,解析失败。
解决方案是在解析引脚号前,先判断后缀字符串是否为纯数字,非纯数字则判定为子电路端口,跳过元件绑定,仅记录导线连接关系。修改后子电路端口解析正常,程序不再崩溃。 - 信号仅解析时传递导致子电路无输出
开发初期,信号传递只在解析连接时执行一次,导致子电路内部元件计算出结果后,无法自动同步到子电路输出端口,主电路始终收不到子电路的输出信号,所有多子电路用例全部失败。
解决方案是引入wireMap全局导线模型,在每次迭代计算时,先遍历所有导线同步信号,让信号可以沿着导线自动传播,跨端口、跨层级自动传递。修改后子电路的信号可以正确传递到主电路,多级嵌套电路计算完全正常。 - 异常优先级顺序错误
最初实现异常检测时,将信号冲突的检测放在了顺序错误之前,导致部分同时存在两类错误的用例,输出的异常类型与预期不符。重新核对题目优先级规则后,严格按照 “多输入源> 无输入源 > 无接收端 > 顺序错误 > 信号冲突” 的顺序调整检测代码,先检测高优先级错误,发现后直接返回,不再检测后续低优先级错误。调整后所有异常用例的输出完全符合题目要求。 - 未定义节点误判为信号源
最初判断引脚类型时,所有不带-的字符串都被当成了顶层输入信号源,导致很多未定义的非法节点被误判为有效源,异常检测失效。解决方案是新增topInputs集合,在解析 INPUT 行时记录所有合法的顶层输入名称,判断引脚类型时,只有集合内的名称才算有效信号源,其余均判定为非法。修正后异常检测的准确率大幅提升。
四、改进建议
代码结构优化
- 引入简单工厂模式封装元件创建
当前三个版本的元件创建逻辑都散落在CircuitHandler中,以长串if-else分支的形式存在,每次新增元件都需要修改处理器代码,违反开闭原则。可以引入简单工厂模式,设计GateFactory工厂类,将所有元件创建逻辑封装到工厂类中,对外仅暴露createGate方法。CircuitHandler仅通过工厂获取元件实例,完全不需要感知具体的创建逻辑。
这种优化不仅可以消除处理器中的长分支判断,还可以将元件创建的职责独立出来,符合单一职责原则,后续新增元件时仅需修改工厂类,不影响核心调度逻辑。 - 输出逻辑独立为报告生成类
当前所有版本的输出逻辑都内嵌在CircuitHandler中,不同元件的输出格式判断混杂在一起,代码可读性差,修改输出格式时容易误改其他逻辑。可以将输出逻辑抽离为独立的ReportGenerator类,专门负责元件排序、格式拼接、结果打印,提供统一的print方法接收元件列表。
这种优化实现了业务计算与输出展示的完全解耦,后续如果需要修改输出格式、新增输出项,仅需修改报告生成类,不影响核心计算逻辑,同时也让CircuitHandler的职责更加聚焦。
可维护性优化
- 完善异常体系,使用自定义异常类
当前 V3 版本的异常处理仅通过字符串记录错误信息,表达方式原始,扩展性差。可以设计自定义异常类体系,创建基类CircuitException,派生出MultipleInputException、NoInputException、SequenceErrorException、SignalConflictException等具体异常子类,每个异常类封装自身的错误信息与优先级。
在连接解析时检测到错误直接抛出对应异常,在Main方法中统一捕获处理。这种方式比字符串记录更规范,类型安全,扩展新异常类型时也更清晰,同时可以附带更多异常上下文信息,便于调试排查。 - 提取常量与枚举,消除魔法值
当前代码中存在大量硬编码的魔法值,比如元件类型顺序字符串、引脚编号常量、异常提示文本等,散落在代码各处,修改时容易遗漏。可以提取专门的常量类,或者使用枚举类型进行管理,比如定义GateTypeEnum枚举管理所有元件类型与优先级,定义PinConstants接口管理引脚编号常量,定义ErrorMsgConstants管理异常提示文本。
常量统一管理后,修改配置时仅需修改一处,避免多处修改导致的不一致问题,同时代码可读性也会显著提升。
可扩展性优化
- 完善组合模式,统一组件接口
当前子电路与基础门只是隐式的组合关系,没有统一的抽象接口,子电路还不能像普通元件一样被直接调度,嵌套层级受限。可以定义统一的CircuitComponent抽象组件接口,规定getInputPorts、getOutputPorts、compute等通用方法,基础门与子电路都实现该接口。
完善后,子电路与基础元件具备完全一致的对外接口,可以任意层级嵌套,子电路内部还可以再引用其他子电路,真正实现无限层级的模块化电路构建,程序的扩展性会得到质的提升。 - 扩展时序电路仿真能力
当前程序仅支持组合逻辑电路的仿真,没有状态记忆能力。后续可以学习时序电路的建模方法,新增 D 触发器、JK 触发器等时序元件,引入时钟信号驱动,加入状态更新机制,实现同步时序电路的仿真。在此基础上还可以扩展寄存器、计数器等常用时序模块,让程序的仿真能力覆盖更完整的数字电路场景。

浙公网安备 33010602011771号