数字电路系统

一、前言
这次数字电路仿真分三次作业,难度一次比一次高,代码量也是慢慢堆起来的。最开始我只会写最简单的几个逻辑门,到后面能写带控制引脚的复杂元件,最后还用上了组合模式做子电路、加各种输入报错检测,相当于完整走了一遍小型仿真项目从 0 到 1 的开发流程。
作业 1(基础逻辑门版):是整个项目的入门,只做与、或、非、异或、同或五个基础门,难度最低。主要就是练面向对象封装,搞懂单一职责到底该怎么用,把数据、运算、调度分开写。
作业 2(组合元件扩展版):在作业 1 基础上加了一堆新器件,三态门、译码器、数据选择器、分配器全都补上,还多了控制引脚、多路输出、无效高阻这些概念,代码结构一下子复杂很多,最难的是区分每种元件不同引脚的作用,运算逻辑也各不一样。
作业 3(子电路 + 异常校验完整版):三次里最难的一次。不仅要兼容前面所有元件,还要用组合模式实现子电路复用,另外新增一大堆连线异常判断,只要输入格式不对、信号冲突就要按优先级输出报错。把电路复用、程序容错结合到一起,对代码严谨性要求特别高。

三次作业一层一层往上叠加功能,不光巩固了 Java 基础语法,更重要的是实打实搞懂了面向对象设计到底能解决什么实际问题。

二、设计与分析
(一)作业 1:基础逻辑门数字电路仿真
1.需求简单回顾
只实现五种基础逻辑门,每种门有固定的输入输出规则。输入会给每个门的定义、外部输入高低电平、线路连接关系,最后输入 end 结束。程序要按规定顺序输出每个门的输出电平,如果引脚没接全信号就直接跳过这个元件。

2.类结构设计与分析
写代码的时候我特意拆分成好几个类,严格按照老师说的单一职责来写:
1.Gate 抽象父类:把所有门共用的东西全放这里,比如元件编号、所有引脚、每个引脚对应的电平,还有给引脚赋值、读取所有输入电平的通用方法。最后留一个抽象计算输出的方法,每个门自己实现。
2.And/Or/Not/Xor/Xnor 五个子类:只重写计算输出的函数,各自实现自己的逻辑,别的公共代码全复用父类,不用重复写。
3.DigitalCircuitSim 主类:专门负责读输入、解析每一行数据、传递各个引脚的电平信号,最后统一排序输出结果。
image
image

代码里大量用了 Map,gateMap 存所有门,靠元件名字直接找到对应对象;outerPin 存 A、B 这种外部输入信号;pinValue 记录每个引脚的电平。我还用 IDEA 的 Statistic 插件统计代码行数,整体代码不多,结构看着很清爽。

3.写代码时的关键思路和心得
1.继承 + 抽象类:五个门共用一套引脚管理代码,后续想新增简单逻辑门,只需要新建子类重写计算方法就行,不用大改主程序。
2.Map 容器的优点:不像数组只能按数字下标找,这里引脚、元件都是自定义字符串名字,用 Map 一键匹配,不用循环遍历查找,写起来省事很多。
3.多级电路自动计算:每次连线赋值的时候,当场算出源元件的输出电平,不用手动处理计算顺序,天然支持好几层门互相级联。

这次做完我才算真正明白单一职责的好处。最开始我把所有代码全堆在 Main 里,改一点东西整个程序都容易崩;拆分完各个类之后,改排序、改逻辑运算互不干扰,代码可读性直接提升一大截,也意识到好的设计不是一次写出来的,都是慢慢拆分、调整职责改出来的。

(二)作业 2:带控制引脚的组合元件扩展版
1.需求简单回顾
在作业 1 五个基础门之上,新增三态门、译码器、数据选择器、数据分配器四种器件。多了控制引脚这个新概念,每个元件引脚还要按「控制、输入、输出」固定顺序区分,同时新增无效高阻状态,每种元件输出格式也不一样,译码器、分配器都有特殊输出要求。

2.类结构设计与分析
整体架构沿用作业 1 的抽象 Gate 父类,新增四类元件子类,同时扩充引脚管理逻辑,区分控制引脚、输入引脚、输出引脚三类:
1.三态门:单独处理控制引脚,控制信号为 0 时输出无效,直接忽略该元件;
2.译码器:存储多路输出引脚,运算后只输出唯一输出 0 的引脚编号;
image
image

主程序新增引脚分类解析逻辑,读取元件名称时区分基础门、译码器、选择分配器,自动生成对应数量的控制、输入、输出引脚,整体类数量变多,但每个类只管好自己一种元件的运算,不会乱。

3.写代码时的关键思路和心得
1.引脚分类是最大难点:一开始没分清控制脚和输入脚,直接把所有引脚混在一起计算,结果输出全错。后来单独拆分列表存储控制引脚、输入引脚,运算时分开读取,才把逻辑理顺。
2.无效状态处理:之前只有 0 和 1 两种电平,这次多了高阻无效值,我新增枚举统一标识三种信号状态,避免用 - 1 这种魔数,代码更好看懂。
3.不同元件差异化输出:每种器件输出规则完全不一样,我在父类里统一预留输出方法,每个子类重写输出逻辑,主程序只需要统一调用,不用写一堆 if 判断区分元件。

做完这次作业我才发现,真实硬件电路里控制引脚是很常见的设计,代码里通过拆分不同类型引脚、区分有效 / 无效信号,能很好模拟硬件真实工作状态;同时也学会了如何在原有稳定代码上扩展新功能,不用推翻重写。

(三)作业 3:支持子电路 + 连线异常校验完整版
1.需求简单回顾
这次是三次作业里最难的,兼容前面所有九种电路元件,新增两大核心功能:一是子电路,可以把一段电路打包复用,主电路里直接调用;二是完整的连线异常检测,一共五种错误,有固定优先级,遇到多条错误只输出最靠前、优先级最高的那条。
同时新增组合模式设计要求,把普通门和子电路统一当成电路单元,子电路内部也能独立存放元件、连线、输入输出映射。输出格式也调整,子电路内部元件输出要带上子电路编号。

2.类结构设计与分析
按照推荐的组合模式重构顶层结构:
1.顶层抽象:不管是普通逻辑门,还是子电路,都继承这个类,统一提供读取引脚信号、计算输出的方法;
2.基础门作为叶子节点:And、Or、三态门、译码器这些都属于叶子,不能再拆分;
3.子电路作为组合节点:内部可以存放多个各种门,自带局部输入、输出映射、内部连线,对外暴露引脚给主电路调用。
image
image

主程序读取输入时,先全部解析完所有子电路定义,再读取主电路的输入和连线;解析每一条连线时先交给校验工具检测,一旦发现异常直接打印,终止后续解析。

3.写代码时的关键思路和心得
1.组合模式太适合子电路复用:把子电路和普通门归为同一类抽象节点,主电路不用区分调用的是普通元件还是子电路,调用逻辑完全统一,拓展性拉满。
2.异常校验踩了很多坑:最开始没按优先级判断,多条错误同时出现时输出提示混乱,后来按题目规定顺序依次判断,一旦匹配到高优先级错误直接返回,不再往下判断。
3.信号冲突检测:用 Map 记录每个输入引脚对应的信号来源,新增连线时先判断引脚是否已经绑定过源,重复就抛出冲突错误,从源头避免一个引脚接收多路信号。

这次作业让我体会到工程代码两个很重要的点:一是代码复用,子电路相当于封装好的模块,不用重复写相同电路;二是程序健壮性,输入校验不能省略,不然随便输错一点格式程序直接崩,完善的报错提示能快速定位问题。

三、踩坑心得
作业 1 基础逻辑门踩坑
1.悬空引脚判定:每个元件输入引脚是连续编号 1~n,必须逐个遍历全部引脚。只要任意一个引脚没有接入信号,电平为 - 1,该元件就直接失效,不参与输出导致错误。所以必须严格遍历该元件规定的全部输入引脚,不能只处理连上线路的引脚。
2.连线格式 + 输入引脚多驱动冲突:一条连线必须第一个为信号源,后面全部是目标引脚,否则输出就会出错。所以要先完成所有输入合法性校验,再执行电平计算。

作业 2 组合元件扩展踩坑
1.控制引脚和输入引脚混为一谈:写译码器、三态门的时候,把控制引脚当成普通输入一起计算,输出结果完全错乱。单独分开存储两类引脚,运算时分开取值才修复。
2.无效高阻状态忘记处理:三态门控制信号为 0 时,我最开始还是正常计算输出,不符合题目要求。新增信号枚举,专门区分 NONE 无效状态,判断到无效直接跳过该元件输出。
3.各类元件输出格式混淆:译码器输出引脚编号、分配器输出一串字符,最开始统一输出 0/1,和题目要求不符。给每个子类重写专属输出方法,区分不同器件打印规则。

作业 3 子电路 + 异常校验踩坑
1.子电路引脚映射逻辑混乱:子电路内部输入输出和主电路引用的引脚对应关系写错,信号传不过去。单独建 Map 存储子电路对外引脚和内部引脚的映射,每次传递信号时做转换。
2.异常优先级处理错误:多条连线同时存在多种错误时,低优先级错误先输出,和题目要求相反。调整判断顺序,匹配到高优先级错误直接返回,不再校验后续错误。
3.输入引脚多路信号冲突漏判:两条连线给同一个输入引脚赋值,程序没有检测,电平来回覆盖。新增全局映射表,记录每个输入引脚的信号来源,重复赋值直接报冲突错误。

四、改进建议
回头看三次写的代码,还有不少地方可以优化:
1.现在没有自定义异常,出错只是打印文字然后 return 退出。后面可以自定义电路异常类,抛出异常后统一捕获处理,错误提示能写得更详细。
2.代码里很多固定字符串、数字都是硬编码,比如引脚前缀、元件标识、控制引脚数量,后续可以提取成全局静态常量,统一修改不用到处翻代码。
3.部分计算输出的方法代码偏长,逻辑堆在一起,可读性一般。可以拆分成多个小型辅助方法,每个方法只做一件小事,方便后续调试查错。
4.算法层面现在没有优化,所有信号每次都重复计算,电路规模大了会重复运算。可以加缓存保存每个元件计算后的输出,没有信号变更时直接复用缓存结果。

五、总结
1.收获
从只会写一堆杂乱代码的新手,到能分层、分模块设计完整仿真程序,三次迭代下来我才算吃透 Java 面向对象的核心。抽象、继承、多态、组合模式、Map 集合容器这些知识点不再是纸上概念,全都落地到电路仿真里。同时学会了分层处理输入、校验、运算、输出完整业务流程,能看懂类之间的关联、聚合、组合关系。
2.自身不足
第一,做测试的时候只会简单跑样例,很少主动测边界情况,很多隐藏 bug 只有复杂输入才会暴露;第二,算法基础薄弱,只会简单遍历,没有优化思路;第三,拿到复杂需求时,很难第一时间理清各个类之间的关联,需要反复调整结构。
之后打算多练各类基础算法,平时写小程序主动多设计边界测试用例,多看组合、聚合这类设计案例,锻炼自己拆解复杂业务、设计类结构的能力。

posted @ 2026-06-24 21:01  ljh2015  阅读(5)  评论(0)    收藏  举报