三次Pta实验总结

PTA-- 三次数字电路模拟作业总结

一、前言

知识点覆盖
本单元三次作业以数字电路模拟为核心场景,采用迭代式开发的模式,从基础逻辑门逐步扩展到组合元件、子电路与异常检测,覆盖的知识点层层递进:
作业集 4(数字电路模拟 - 1):Java 面向对象基础封装、集合框架(ArrayList)应用、字符串解析与处理、五种基础逻辑门运算原理、迭代式信号传播算法、冒泡排序实现、输入输出格式控制。
作业集 5(数字电路模拟 - 2):枚举类型应用、多类引脚(控制 / 输入 / 输出)建模、多输出元件逻辑实现、译码器 / 数据选择器 / 数据分配器工作原理、高阻态无效值处理、多格式输出适配、递归求值与记忆化缓存。
作业集 6(数字电路模拟 - 4):组合模式设计思想、子电路模块化封装与端口映射、命名空间隔离机制、有限状态机解析输入、多优先级异常检测与处理、递归作用域区分、前缀化命名管理。

整体来看,三次作业不仅锻炼了 Java 代码实现能力,更强调面向对象设计原则在迭代开发中的应用,以及对复杂业务逻辑的拆解与边界处理能力。

整体迭代思路

迭代 1:搭建电路模拟的基础框架,实现五种基础逻辑门的建模与信号传播,完成输入解析、稳定计算、排序输出的完整流程,验证核心逻辑可行性。
迭代 2:扩展元件体系,新增三态门、译码器、数据选择器、数据分配器四类元件,引入控制引脚与高阻态概念,统一引脚编号规则,同时优化求值算法,将迭代遍历改为递归缓存,提升计算效率。
迭代 3:升级电路结构层级,新增子电路定义与引用功能,支持模块化复用;新增全量异常输入检测机制,按优先级输出错误信息;同时处理子电路命名空间隔离问题,保证元件作用域互不干扰。

题量与难度评价
三次作业均为单道综合编程题,题量看似单一,但代码规模与实现难度逐次递增。

第一次作业代码量约 300 行,难度标注为 “较难”,核心难点在于理解信号传播的迭代逻辑;

第二次作业代码量约 500 行,难度为 “困难”,难点集中在多类元件的引脚映射与不同输出格式的适配;

第三次作业代码量约 600 行,难度同样为 “困难”,难点在于子电路的作用域处理与多优先级异常判定,边界情况极多。

整体而言,三次作业呈阶梯式上升,每一次迭代都需要对原有代码进行一定程度的重构,十分考验代码的可扩展性与设计合理性。

二、设计与分析

(一)第一次作业:数字电路模拟程序 - 1

作业要求回顾
实现与门、或门、非门、异或门、同或门五种基础逻辑门的电路模拟,支持多输入引脚的与门、或门;解析输入信号与连接关系,通过迭代计算得到电路稳定状态;最终按 “与门→或门→非门→异或门→同或门” 的顺序,同类按编号从小到大输出有效元件的输出电平,输入未接全的元件忽略输出。
类图设计
plantuml-diagram (7)
类职责拆分


Circuit 类:遵循数据与逻辑封装原则,每个元件对象自身维护输入引脚电平与输出值,提供caculate()方法完成自身的逻辑运算,isValid()方法判断输入是否全部有效,外部无需关心元件内部运算细节。

Input 类:作为工具类,集中处理所有字符串解析工作,将原始输入行转化为元件对象与连接关系,解耦解析逻辑与业务实体。

Main 类: 仅负责流程编排,不包含具体解析与运算逻辑,串联起 “读取→构建→计算→排序→输出” 的完整链路。

核心机制 :迭代式信号传播

采用循环迭代的方式模拟信号传播:反复遍历所有元件计算输出,再遍历所有连接同步信号到下游引脚,直到一轮循环中没有任何信号发生变化,判定电路达到稳定状态。该方案无需手动构建元件依赖拓扑,实现门槛低,天然适配组合逻辑电路的信号传导特性。


复杂度分析
方法级复杂度

image-20260624222818310

设计心得
第一次作业以快速实现功能为目标,采用简单的分层结构能够快速完成开发,但也暴露出明显的设计缺陷:Input 类职责过于臃肿,同时承担了信号解析、元件创建、电平更新三项工作,不符合单一职责原则;迭代式计算虽然实现简单,但存在大量重复遍历,大规模电路下效率较低,为后续功能扩展埋下了隐患。

(二)第二次作业:数字电路模拟程序 - 2

作业要求回顾

在第一次作业基础上,新增三态门、译码器、数据选择器、数据分配器四类元件;

引入控制引脚概念,统一引脚按 “控制→输入→输出” 的顺序编号;

不同元件采用差异化输出格式:普通门输出引脚电平,译码器输出低电平引脚编号,数据分配器按顺序输出所有引脚状态,无效位用 “-” 表示;

控制引脚无效或高阻态的元件忽略输出。

类图设计
plantuml-diagram (8)
方法级复杂度

image-20260624223039225 **类级复杂度**

image-20260624223126068

设计心得
递归求值的重构让信号传播逻辑更加清晰,计算效率也得到了提升,但所有工具方法全部堆积在 Main 类中,导致类职责严重臃肿,违反单一职责原则。枚举类型的引入确实提升了代码可维护性,新增元件时只需扩展枚举与计算分支即可,但输出格式处理逻辑散落在主函数中,业务运算与输出展示高度耦合,后续新增元件仍需修改主流程代码。

(三)第三次作业:数字电路模拟程序 - 4

作业要求回顾
在第一次作业基础上,新增子电路定义与引用功能,子电路拥有独立的输入输出端口,可在主电路中作为模块复用;

新增五类输入异常检测,按固定优先级输出第一个出现的异常;

子电路内部的元件输出时需带上子电路编号前缀;所有子电路定义位于主电路之前。

类图设计
plantuml-diagram (9)
复杂度分析(基于 SourceMonitor)

方法级复杂度
image-20260624223214549
类级复杂度
image-20260624223228279

设计心得

本次作业通过前缀拼接的方式快速实现了子电路的命名隔离,满足了题目要求,但并没有真正采用题目建议的组合设计模式,子电路没有成为独立的可复用元件,无法支持嵌套等高级特性,扩展性不足。

异常检测逻辑硬编码在解析方法中,新增异常类型必须修改原有代码,不符合开闭原则。

同时 Main 类职责过载的问题进一步加剧,代码可维护性有所下降。

三、采坑心得

(一)第一次作业:基础逻辑与迭代计算坑

迭代收敛逻辑缺失
初期编写迭代循环时,只更新了元件的输出值,没有同步将输出信号通过连接更新到下游元件的输入引脚,导致电路永远无法达到稳定状态,程序陷入死循环。

排查与验证:打印每轮循环的元件输出值,发现输出值已经稳定但循环仍不终止,定位到连接信号更新步骤被遗漏。

修复方案:在迭代循环中增加连接更新阶段,先计算所有元件的输出,再遍历所有连接,将源引脚的电平同步到所有目标引脚,同步过程中标记是否发生变化。


元件排序规则偏差
题目要求同类元件按编号从小到大排序,初期直接对元件名字符串进行字典序排序,导致编号位数不同时顺序错误,例如 “A (10) 1” 会排在 “A (2) 1” 前面。

测试验证:构造包含 A (2) 1 与 A (10) 1 的测试用例,预期 A (2) 1 先输出,实际结果相反。

修复方案:解析元件名称时提取类型字符与数字编号,排序时先按类型优先级比较,类型相同时按编号的数值大小比较,而非字符串比较。


无效元件过滤错误

初期实现时,只要元件有一个引脚有值就参与计算,导致输入未接全的元件也会输出结果,不符合样例 6 中 “输入不全的元件忽略” 的要求。

修复方案:新增isValid()方法,遍历元件所有输入引脚,只有全部引脚都为有效电平时才判定元件有效,参与计算与输出,存在未赋值引脚则直接跳过。

(二)第二次作业:元件扩展与引脚映射坑

引脚编号顺序理解错误
题目规定引脚按 “控制引脚→输入引脚→输出引脚” 的顺序编号,初期误将输入引脚排在控制引脚之前,导致译码器的引脚功能完全错位,样例 8 输出结果完全错误。

排查过程:对照样例 8 的输入与预期输出,逐个核对引脚编号对应的功能,发现控制引脚编号应为 0、1、2,输入引脚从 3 开始,输出引脚从更高位开始,与原有实现完全颠倒。

修复方案:重构 Component 类的构造函数,严格按照 “控制引脚→输入引脚→输出引脚” 的顺序分配引脚编号,与题目描述完全对齐。
译码器输出格式不符


译码器要求输出 “输出为 0 的引脚编号”,初期按照普通门的格式输出 “引脚名:电平”,导致格式判分点全部失分。

修复方案:单独处理译码器的输出分支,计算出低电平引脚的序号后,直接输出 “元件名:序号” 的格式,不携带引脚号与电平标识。
高阻态与低电平混淆

三态门关闭时输出高阻态(无效状态),初期将无效值存储为 0,导致下游元件把高阻态当成低电平参与运算,输出错误结果。
修复方案:统一使用null表示无效 / 高阻状态,运算时只要输入列表中包含 null,输出就全部为 null,无效元件不参与最终输出。

(三)第三次作业:子电路与异常检测坑

子电路端口方向判断反转

子电路的 INPUT 端口在子电路内部是信号源(驱动端),在主电路中是信号接收端;

OUTPUT 端口则相反。初期判断引脚类型时没有区分上下文,导致子电路内部的连接被误判为 “无输入”,直接抛出异常。
排查过程:使用样例 1 单步调试,发现子电路内的连接 [A A (2) 1-1] 被判定为无输入,定位到 pinType 方法对子电路内部端口的类型判断完全相反。
修复方案:引脚类型判断方法增加上下文子电路 ID 参数,子电路内部环境下,INPUT 端口视为驱动源,OUTPUT 端口视为接收端;

主电路环境下则反之。


异常优先级顺序错乱
题目规定 5 种异常有严格的优先级,初期按代码书写顺序依次判断,导致低优先级异常先被命中输出。例如一条连接同时存在 “多个输入源” 和 “输入输出顺序错误” 时,应输出优先级更高的 “多输入”,初期却输出了顺序错误。
修复方案:在解析方法中严格按照优先级从高到低的顺序依次检查,每类异常独立判断,命中最高优先级异常后立即返回结果,不再执行后续低优先级的校验逻辑。


子电路元件命名冲突
当子电路与主电路存在同名元件(如都定义了 N1)时,初期没有做命名隔离,导致主电路的元件被后者覆盖,计算结果出现偏差。
修复方案:子电路内的所有元件名、引脚名在存入全局映射前,统一拼接 “Cx-” 前缀,通过命名空间隔离避免冲突,同时保证求值逻辑可以统一处理。


信号冲突检测遗漏
初期仅对主电路的引脚做冲突检测,忽略了子电路内部的引脚,子电路内同一个输入引脚被多个源驱动时无法触发异常。
修复方案:连接解析时统一将引脚名转换为带前缀的全名,再检查驱动映射表,确保所有作用域下的引脚冲突都能被正确检测。

四、改进建议

(一)代码层面改进

查找逻辑优化:HashMap 替代遍历
三次作业中查找元件普遍采用遍历 ArrayList 的方式,时间复杂度为 O (n),元件数量较多时效率低下。

例如第一次作业的getnum方法、第二次作业的元件查找,都可以改用 HashMap 存储,以元件名作为 key,查找复杂度降至 O (1)。

对于引脚驱动关系的查找,也可以通过 HashMap 直接映射,避免循环匹配。
递归改迭代:规避栈溢出风险


第二次、第三次作业采用递归求值,当电路层级很深(如多层子电路嵌套)时存在栈溢出风险。

改进方案:先构建元件的依赖关系图,通过拓扑排序得到计算顺序,再按顺序迭代计算每个元件的输出。既可以避免栈溢出,又能保证每个元件仅计算一次,效率高于递归与迭代遍历。


正则预编译:降低解析开销
元件名解析的正则表达式在每次调用时都会重新编译,存在重复性能开销。
改进方案:将解析用的 Pattern 对象提取为类静态常量,在类加载时完成预编译,复用正则对象,减少重复编译的性能损耗。

(二)设计层面改进

组合模式重构子电路
当前子电路仅通过前缀简单隔离,没有成为真正的可复用元件。参考题目给出的组合模式建议,抽象出IComponent接口,定义统一的求值方法;

基础门电路作为 Leaf 节点实现接口,子电路作为 Composite 节点实现接口,内部管理其子元件列表。

重构后新增子电路无需修改主求值逻辑,支持子电路无限嵌套,完全符合开闭原则,扩展性大幅提升。

拆分 Main 类,落实单一职责

当前 Main 类承担了输入解析、异常检测、信号计算、结果输出四项职责,代码臃肿且难以维护。

改进方案:拆分为四个独立类:

CircuitParser:专注输入解析与异常检测,返回电路对象与异常信息

CircuitSimulator:封装电路计算核心逻辑,提供稳定求值接口

OutputFormatter:负责不同元件的格式化输出

Main:仅保留程序入口与流程调度

拆分后每个类职责单一,代码可读性与可维护性显著提升
策略模式实现元件运算
当前元件运算逻辑都写在 switch 分支中,新增元件必须修改原有代码,违反开闭原则。
改进方案:定义GateLogic策略接口,包含compute方法,为每种元件实现对应的策略类(如AndGateLogic、DecoderLogic),再通过简单工厂根据元件类型获取对应策略实例。新增元件时只需新增策略类,无需修改原有运算代码。

(三)业务功能扩展

时序电路支持
按照题目的迭代规划,可新增 D 触发器、JK 触发器等时序元件,引入时钟信号与状态存储,支持带反馈的时序电路模拟,进一步贴近真实数字电路场景。
可视化交互扩展
可扩展 GUI 界面,支持拖拽放置元件、鼠标连线、实时显示引脚电平,将模拟器从命令行工具升级为可视化教学工具,提升实用价值。
完善异常检测体系
新增更多异常检测项,如元件名非法、引脚号越界、子电路未定义、组合环路检测等,同时补充错误行号与详细原因提示,提升程序的调试友好性。

五、总结

学习收获

(1)技术能力提升

Java 语言层面:熟练掌握了集合框架、枚举类型、正则表达式、字符串处理等基础语法;

深入理解了迭代与递归两种求值方式的优劣,能够根据业务场景选择合适的实现方案;积累了大量边界条件处理与异常排查的经验。

面向对象设计:对单一职责原则、开闭原则有了更具象的理解,亲身感受到了初期设计不足带来的重构成本;初步接触了组合模式、策略模式的应用场景,认识到合理的架构设计对迭代开发的重要性。

业务与工程能力:掌握了数字电路的基本工作原理,能够将硬件逻辑转化为软件模拟实现;学会了拆解复杂需求,分层逐步实现;养成了对照测试用例逐一定位 bug 的调试习惯。

(2)工程实践认知

迭代开发:三次作业的增量迭代模式,让我深刻体会到了小步快跑的优势,每次聚焦少量需求降低开发难度;

同时也暴露了 “先实现后设计” 的弊端,前期架构简陋会导致后期大量返工。

边界意识:从第一次只关注正常输入,到第三次全面处理异常情况,我逐渐认识到,健壮的程序不仅要能处理正确输入,更要妥善应对各类异常场景,且异常处理要有清晰的规则与优先级。

测试的重要性:多次因为边界考虑不全出现样例通不过的情况,让我意识到不能只依赖官方测试点,要主动构造边界用例、极端用例验证代码,才能减少隐藏 bug。

待改进与深入学习的方向


(1)技术层面

设计模式:系统学习经典设计模式,尤其是组合模式、策略模式、工厂模式,在后续开发中主动应用,跳出 “能用就行” 的思维,主动优化代码架构。

算法与数据结构:深入学习图论相关算法,如拓扑排序、最短路径,优化电路仿真的效率;学习更高效的字符串解析算法,提升复杂输入的处理能力。

质量工具:熟练使用 SourceMonitor 等代码质量分析工具,主动关注圈复杂度、耦合度等指标,在编码阶段就控制代码质量,避免写出 “大泥球” 代码。


(2)业务层面

数字电路知识:补充学习时序电路、硬件描述语言等相关知识,深入理解更多电路元件的工作原理,为后续迭代作业打下坚实基础。
编译原理基础:输入解析部分多次出现格式理解错误,后续可以学习词法分析、语法分析的基础知识,更专业地处理复杂格式的输入。

总结感悟
三次数字电路模拟作业,从最基础的五个逻辑门,到支持九种元件、子电路复用与异常检测的完整模拟器,代码量和复杂度不断攀升,也让我在编程能力和工程思维上都完成了一次进阶。从最开始只追求 “跑通样例”,到后来开始主动考虑代码结构、可扩展性、边界异常,我逐渐理解了面向对象设计的真正价值 —— 好的设计不是为了炫技,而是为了让代码在不断迭代的过程中依然保持清晰、可控。
当然,目前的实现还有很多不足:类职责划分不够清晰,设计模式应用不足,性能还有较大优化空间。这些问题也为后续的学习指明了方向。未来我会更加重视前期架构设计,主动践行设计原则,写出更优雅、更易维护的代码。本次作业积累的迭代开发、调试排错、需求拆解经验,也会成为后续课程学习和项目开发的宝贵财富。

posted @ 2026-06-24 22:41  25202124胡雨峰  阅读(4)  评论(0)    收藏  举报