当前位置: 首页 > news >正文

南昌航空大学软件学院第四五六次PTA总结blog

一、前言

本阶段的三次作业是一个完整的迭代开发过程,主题围绕数字电路模拟展开。三次作业的知识点、题量和难度呈现明显的阶梯式递进:

 
 
作业 核心知识点 题量 重点考察
第一次 基本门电路的模拟 中等 类的设计、接口实现
第二次 引入三态门、解码器、多路复用器等复杂组件 较大 工厂模式、组件扩展
第三次 子电路定义与嵌套、连接验证、信号传播 递归解析、图论遍历、错误处理

三次作业环环相扣,从最简单的门电路模拟开始,逐步引入更复杂的组件类型,最后实现了完整的子电路嵌套系统。这个过程不仅锻炼了Java编程能力,更重要的是培养了面向对象的设计思维

 

二、设计与分析

2.1 第一次作业:基本门电路模拟

设计思路:

第一次作业要求模拟五种基本门电路(AND、OR、NOT、XOR、XNOR)。我采用了接口+实现类的设计模式每个门电路类都实现了setPin()getOutput()方法,通过引脚编号管理输入值。值得一提的是,我引入了缓存机制cachedOutputcomputed字段),只有在输入发生变化时才重新计算输出,这在后续复杂电路的模拟中能显著提升效率。

SourceMonitor分析:

OCavg WMC
AndGate 2.0 8
OrGate 2.0 8
NotGate 1.8 7
XorGate 2.2 9
XnorGate 2.2 9
Main 4.5 36

可以看到,各个门电路类的圈复杂度控制得很好,平均复杂度在2.0左右。但Main类的simulate()方法复杂度较高(v(G)=12),因为它承担了信号传播的核心逻辑。

UML类图:

PTA4

 

测试用例:

屏幕截图 2026-06-20 115229

心得:
第一次作业让我深刻理解了接口的作用——它的统一使得后续扩展新的组件类型变得非常容易。同时,缓存机制的设计也让我认识到,好的设计不仅要考虑功能正确性,还要考虑性能。

 

2.2 第二次作业:引入复杂组件

设计思路:

第二次作业新增了三态门(TriState)、解码器(Decoder)、多路复用器(Mux)和解复用器(Demux)。这些组件的输入输出逻辑比基本门电路复杂得多:

  • TriState:有控制引脚C和数据引脚IN,当C=1时输出IN,否则高阻态

  • Decoder:有3个控制引脚和n个地址引脚,输出选中的通道号

  • Mux:有k个控制引脚和2^k个数据引脚,根据控制信号选择一路输出

  • Demux:有k个控制引脚和1个数据输入,将数据分发到2^k个输出通道

为了统一管理这些组件的创建,我引入了工厂模式

UML类图:

PTA5

测试用例:

屏幕截图 2026-06-20 115316

心得:
工厂模式的使用让Main类与具体组件类解耦,新增组件类型时只需修改工厂类,符合开闭原则。但同时我也发现,Decoder和Mux的setPin()方法逻辑复杂(需要区分控制引脚和数据引脚),这提示我应该在设计引脚映射时更加规范化。

 

2.3 第三次作业:子电路与系统集成

设计思路:

第三次作业是前两次的集成,要求支持子电路定义和嵌套。这是最复杂的一次,涉及:

  1. 子电路解析:解析Cid: INPUT:... OUT:... [连接] endc格式

  2. 连接验证:检查每个连接是否有且仅有一个输入(驱动源),至少一个输出

  3. 信号冲突检测:确保每个接收端只被一个信号驱动

  4. 子电路展开:将子电路内部的连接展开到主电路中

  5. 信号传播:基于图遍历的BFS算法

UML类图:

PTA6

测试用例:

屏幕截图 2026-06-20 115610

 

SourceMonitor分析:

OCavg WMC 说明
CircuitSimulator 4.8 58 主控类,复杂度合理
SubCircuit 2.5 10 子电路类,清晰简洁
Connection 1.0 2 纯数据类
CircuitComponent 3.2 16 组件类,包含解析和计算
 
 
方法 ev(G) iv(G) v(G) 说明
parseSubcircuits() 5 8 10 子电路解析,分支较多
parseMainCircuit() 4 7 9 主电路解析
CircuitComponent.parse() 8 10 12 组件名称解析,复杂度最高
propagateSignal() 3 6 7 信号传播,逻辑清晰
checkSingleConnError() 4 5 6 连接验证

心得:
第三次作业让我深刻体会到复杂系统的挑战。通过重构,我将功能拆分到四个类中,使得代码更加清晰。但CircuitSimulator类仍然承担了过多职责(58个方法),未来可以进一步拆分。同时,CircuitComponent.parse()方法的圈复杂度高达12,说明组件名称的解析逻辑过于复杂,可以考虑使用策略模式状态模式来简化。

三、采坑心得

3.1 坑一:引脚编号的误解

问题描述:
在第一次作业中,我最初认为组件的输出引脚编号是1,但实际上是0。这导致信号传播时找不到输出。

错误代码:

java
// 错误:输出引脚编号为1
cachedOutput.add(name + "-1:" + out);

正确代码:

java
// 正确:输出引脚编号为0
cachedOutput.add(name + "-0:" + out);

教训: 仔细阅读题目要求,特别是引脚编号规范

3.2 坑二:子电路展开时的命名冲突

问题描述:
在第三次作业中,展开子电路时,我最初直接使用了子电路内部的引脚名,导致与主电路中的同名引脚冲突。

错误做法:

java
// 直接使用子电路内部引脚名
newPins.add(pin);  // 可能与其他子电路的引脚同名

正确做法:

java
// 添加子电路ID前缀
newPins.add("C" + cid + "-" + pin);

3.3 坑三:信号传播的死循环

问题描述:
在实现信号传播时,我最初没有检测循环依赖,导致在某些电路中陷入死循环。

错误代码:

java
while (!queue.isEmpty()) {String u = queue.poll();for (String v : adj.get(u)) {if (!signal.containsKey(v)) {signal.put(v, uVal);queue.add(v);  // 可能无限循环}}
}

正确做法:

java
while (!queue.isEmpty()) {String u = queue.poll();for (String v : adj.get(u)) {if (!signal.containsKey(v)) {signal.put(v, uVal);// 只有新赋值的节点才加入队列if (pinToComp.containsKey(v)) {// 检查组件输入是否全部就绪// ...}queue.add(v);}}
}

这种情况下,A(2)1的两个输入都来自a,不会形成循环。但如果设计不当,可能会出现A->B->A的循环。

教训: 图遍历时必须有访问标记,防止重复处理。同时,对于组合逻辑电路,不应该有循环依赖。

3.4坑四:连接验证的边界条件

问题描述:
在验证连接时,我最初没有考虑[pin]这种只有一个引脚的情况。

错误:

text
[pin]  // 只有一个引脚,既无输入也无输出

正确验证:

java
if (pins.size() < 2) {// 至少需要两个引脚:一个驱动源,一个接收端System.out.println("ERROR: " + connStr + " invalid connection");return true;
}

教训:需要全面考虑各种异常情况。

四、改进建议

4.1 增强错误处理

当前代码中大量使用了try-catch吞掉异常,这不利于调试:

java
try {// 可能出错的代码
} catch (Exception e) {// 什么都不做,静默失败
}

建议改为明确抛出有意义的异常

java
try {// 可能出错的代码
} catch (NumberFormatException e) {throw new CircuitParseException("Invalid number format in: " + name, e);
}

4.2 引入设计模式

  • 访问者模式:用于遍历电路结构,执行不同的操作(如验证、展开、传播)

  • 观察者模式:用于信号变化时的通知,替代当前的轮询方式

  • 建造者模式:用于构建复杂的子电路对象

  • 策略模式:用于简化组件名称的解析逻辑

 

五、总结

通过这三次作业,我完成了从"面向过程"到"面向对象"的思想转变:

5.1 学到的知识

  1. Java核心语法:接口、抽象类、继承、多态、泛型、集合框架

  2. 设计模式:工厂模式、单一职责原则、开闭原则

  3. 数据结构:图(邻接表)、队列(BFS)、哈希表

  4. 算法:拓扑排序(隐式)、信号传播算法

  5. 工程实践:代码度量、类图设计、单元测试

5.2 需要进一步学习的领域

  1. 设计模式:深入学习访问者模式、观察者模式等更复杂的模式

  2. 测试驱动开发(TDD):先写测试用例,再写实现代码

  3. 并发编程:多线程环境下的电路模拟

  4. 编译原理:词法分析、语法分析

5.3 感悟

三次作业的迭代过程,让我深刻理解了软件开发不是一蹴而就的。第一次作业可能只需要考虑功能正确性,第二次需要考虑扩展性,第三次则需要考虑系统的整体架构。每一次迭代都是对前一次的重构和提升

正如我在第三次作业中所体会到的,当系统复杂度超过某个阈值时,良好的设计比高级的算法更重要。一个清晰的架构可以让后续的修改和扩展变得容易,而一个混乱的架构会让任何修改都变得复杂。

通过这三次作业,我也体会到了代码度量的重要性。SourceMonitor提供的圈复杂度、耦合度等指标,为后续的重构提供了方向。

最后,感谢这三次作业给我带来的成长。从最初对"面向对象"的懵懂理解,到现在能够设计出具有一定复杂度的系统,这个过程虽然艰辛,但收获是巨大的。未来的学习中,我将继续在代码质量系统设计方面下功夫。

http://www.jsqmd.com/news/1048577/

相关文章:

  • 自然人身份确权元数据集合赋能医疗健康证照合规
  • 抛弃传统RAG:LLM Wiki才是Agent真正的知识大脑
  • emWin高级控件实战:LISTWHEEL与MENU的嵌入式GUI开发指南
  • Codex SDK控制台日志解析实战指南:从错误码到性能预警
  • 3.4.4 使⽤索引扫描来做排序
  • 欧洲卡车模拟2官方中文|V1.60.1.0s+北境地平线DLC+全DLC
  • Adobe-GenP技术深度解析:通用补丁机制与批量激活实现原理
  • 求职简历 PPT 模板怎么选?实测优选百度文库 AI 智能模板,覆盖全行业高效落地
  • 有哪些AI论文网站是真的坚守学术严谨,而不是通用套壳?
  • Gemini 3.1 Flash-Lite端侧推理实战指南
  • 特朗普手机发布一周年仍未到手,合作公关公司不再协助,发布范围成谜
  • Python毕业设计-基于 Python 的智慧文旅信息发布管理平台的设计与实现 基于 Python 技术的文旅资源公开管理系统(源码+LW+部署文档+全bao+远程调试+代码讲解等)
  • 嵌入式GUI开发实战:emWin图形库从零集成与配置指南
  • bili2text深度解析:从B站视频到结构化文本的技术实现与架构设计
  • 数字拼写转换:从规则解析到多语言自动化实现
  • DuckDB:从研究项目到广泛应用的数据库,为何如此之快?
  • 终极掌控:使用SMUDebugTool深度调优AMD Ryzen处理器的完整方案
  • 如何在OBS Studio中集成专业VST音频插件提升直播音质
  • 十二层PCB打样难?看看他三个月如何搞定交付
  • 水电工培训哪里强?1个月从零到师傅,高薪就业不迷茫! - 湖南阳光技术
  • AI工具会越来越多,真正的竞争力是那层让工具跑起来的底座
  • 下载AC FUN视频资源
  • 视觉驱动UI自动化:从DOM到像素的革命性跨越
  • 网盘直链下载助手:告别限速烦恼,九大网盘高速下载全攻略
  • 第16章 MemGPT / Letta —— Agent 记忆的「操作系统」
  • NeuroRebuild™实景动态重构引擎 技术白皮书
  • 2026扬州本地正规瓷砖空鼓维修服务商盘点|无损免拆砖修复,全域上门售后有保障 - 宅安选房屋修缮
  • 终极指南:5分钟掌握Cpp2IL逆向Unity IL2CPP的完整教程
  • 2026年6月最新劳力士中国官方售后客户地址热线电话服务网点 - 劳力士服务中心
  • 抖音无水印下载神器:3分钟学会批量保存高清视频的必备工具