作业集4~6总结Blog
一、前言
这次习题集我围绕数字电路模拟程序做了三次一步步升级的作业,分别是:作业4先做最基础的逻辑门仿真、作业5加了更多复杂的组合电路元件、作业6加上了子电路复用和错误检测功能。三次作业一次比一次难,要求也越来越高,难度呈阶梯式上升,层层递进、环环相扣。本文将结合三次作业的设计思路、实现过程、遇到的问题及改进方向进行全面总结,梳理收获与不足,为后续编程学习积累经验。
下面我会从作业整体情况、每一次作业的设计思路、写代码时踩过的坑、后续怎么改进、以及这次学习的收获这几个方面,完整总结一下三次作业的过程,说说自己遇到的问题和学到的东西。
1.1 三次作业整体概况
三次作业的升级逻辑特别清晰:先做单个功能 → 用继承多态加新功能 → 用组合模式做大架构+加错误处理,每一次都要在之前的代码上改,不能全部推倒重来
-
作业4(基础版):主要写五种最基础的逻辑门,考察怎么把东西封装成类、怎么用队列传递信号、怎么解析题目给的文本格式。难度中等偏上,最头疼的是怎么让信号在电路里自动传下去,一开始经常卡住或者重复计算。
-
作业5(扩展版):在基础门电路上加了四种带控制引脚的复杂元件,考察继承和多态怎么用、不同元件的引脚怎么对应、多输出的结果怎么格式化。难度偏高,最坑的是每个元件的引脚编号规则都不一样,很容易搞混,一错结果就全不对。
-
作业6(完整版):是最后一个版本,加了两个核心功能:可以把常用电路做成子电路重复用、能自动检测五种常见错误,而且必须用题目要求的组合模式来重构整个代码。考察递归怎么写、错误优先级怎么判断。难度特别高,最费劲的是子电路嵌套的时候信号怎么传,还有错误检测的顺序不能乱。
总的来说,这三次作业对我这个小菜鸡来说难度堪比国足出战世界杯,但同时也是对自己学到的知识的检验,经过这三次作业我收获还是蛮大的
二、分阶段作业设计与详细分析
2.1 作业4:基础逻辑门模拟
2.1.1 核心要做的事
这次作业是整个程序的底子,目标是不用专业的电路软件,用代码自己模拟数字电路的运行。主要实现四个功能:
- 能读懂题目给的输入格式:包括最开始的输入信号、方括号里的引脚连接关系;
- 程序自己能认出电路里有哪些元件,根据引脚的连接关系,自动把信号从上游传到下游;
- 按照每种门电路的规则,算出每个元件最终输出的是0还是1;
- 按固定顺序输出结果:先输出与门、再或门、再非门、再异或门、最后同或门,同一种元件按编号从小到大排。
2.1.2 代码的类结构设计
这次我严格按照老师说的“一个类只干一件事”的原则,把代码拆成了七个类,每个类管自己的事,不互相干扰:
| 类名 | 主要干什么 |
|---|---|
| Gate抽象父类 | 把所有门电路共有的属性和方法抽出来,规定好接收信号、判断输入是否全了、计算输出这三个方法的格式,后面加新门电路直接继承就行 |
| 五大门电路子类 | 就是AndGate、OrGate这些,分别写自己的计算逻辑,比如与门就是全1才出1 |
| Circuit类 | 管整个电路里所有的元件和连线,提供加元件、找元件、加连线这些功能 |
| CircuitParser类 | 读题目给的输入文本,一行行解析成信号、连线、元件,自动创建对应的对象,拼成一个完整的电路 |
| Simulator类 | 用队列来传递信号,自动驱动信号一步步往下传,不用我们手动排电路的先后顺序 |
| ResultFormatter类 | 按题目要求的顺序排好元件,把结果整理成规定的格式输出 |
| Main主类 | 程序入口,把上面所有类串起来,从读输入到输出结果走一遍流程 |
PowerDesigner类图:


2.1.3 代码质量检测
我用SourceMonitor工具测了一下代码质量,结果如下:

2.1.4 写完后的反思
这次作业让我第一次体会到面向接口编程的好处,因为有了统一的Gate父类,后面加新元件的时候,仿真器的代码一点都不用改,省了好多事。还有用队列传信号的方法特别聪明,不管电路是什么结构,信号都能自动传下去。但也有很多问题:
- 解析文本的时候用的是最笨的字符串切割方法,只能解析简单的格式,稍微有点不一样就崩了;
- 完全没做错误检查,只要输入格式不对,程序直接就退出了;
2.2 作业5:多类型组合元件扩展
2.2.1 这次加了什么新需求
在作业4的基础上,这次加了四种更复杂的电路元件,不再是只有一个输入一个输出的简单门了:
- 新增元件:三态门、译码器、数据选择器、数据分配器,都是上课学过的组合电路;
- 引脚分成了三种:控制引脚、输入引脚、输出引脚,每个元件的引脚编号都有固定的顺序,不能乱;
- 有些元件有多个输出,输出格式也不一样:译码器只输出哪个引脚是0,数据分配器没用的引脚要输出“-”;
- 更新了输出顺序,把新增的四种元件也加进去了。
2.2.2 代码的类结构设计
原来的Gate父类已经不适合新元件了,所以我把它升级成了Component顶层抽象类,所有元件不管是简单的还是复杂的,都继承这个类,最大化利用继承和多态的好处,加新功能不用改旧代码:
- 顶层Component类:不管是单输出还是多输出、有没有控制引脚的元件都能用,统一管理所有引脚;
- 原来的五种基础门电路直接继承这个新的父类,原来的计算逻辑一点都不用动;
- 四种新元件各自写一个子类,分别实现自己的判断输入是否全了、计算输出、格式化结果的方法。
PowerDesigner类图:
![2c4b9f4b6a5ada47aef7ea0b69b55eb2]()
![7a089b2f90c33aa9bc9742d753c9266e]()
![3cc279e95f7fa12dac69823d2b0a68d6]()
2.2.3 代码质量检测
我用SourceMonitor工具测了一下代码质量,结果如下:

2.2.4 作业 5 总结
这次作业让我真正感受到了继承和多态的威力,因为有了统一的 Component 父类,加四种新元件的时候,仿真器和电路管理类的代码几乎没改,只需要加四个新的子类就行,比重新写一遍省事太多了。但也踩了超级大的坑:每个元件的引脚编号规则都不一样,引脚顺序是 “控制→输入→输出”,我一开始搞反了,结果译码器的结果完全不对,调了整整一下午才发现是引脚对应错了。还有多输出元件的信号分发很容易漏,一开始只传了第一个输出引脚的信号,后面的都没传,导致很多测试用例过不了。
2.3 作业6:子电路+全局异常检测
2.3.1 最终版的核心需求
这次是最后一个版本,主要加了两个特别实用的功能:子电路复用和自动错误检测,而且必须用组合模式来重构整个代码:
- 可以自己定义子电路,把常用的电路组合存起来,后面像用普通门电路一样直接用,不用每次都重新写;
- 子电路里的元件输出结果前面要加上子电路的编号,跟主电路的元件区分开;
- 能自动检测五种常见的电路连接错误,而且错误有严格的优先级,同时有多个错误的时候,只输出最前面的那一个。
2.3.2 代码的类结构设计
这次严格按照组合模式来设计,把普通元件和子电路统一起来,不用分开写两套逻辑:
- 叶子节点:就是基础门电路和各种组合元件,是最小的单元,不能再拆了;
- 容器节点:就是子电路,里面可以放普通元件,也可以再放别的子电路,支持多层嵌套。
所有节点都继承Component顶层抽象类,所以运行的时候,根本不用管当前是普通元件还是子电路,直接调用计算方法就行,子电路会自己递归计算里面的所有元件,代码复用性特别高。
PowerDesigner类图:



2.3.3 代码质量情况
我用SourceMonitor工具测了一下代码质量,结果如下:

2.3.4 作业六总结
作业 6 作为整套迭代作业最难的最终版本,做完之后我也发现了自身很多问题,也是三次作业里踩坑最多、调试最久的一次。首先我一开始对组合设计模式理解很浅,前期没有理清普通元件和子电路的从属关系,分开写了两套仿真代码,不仅代码冗余杂乱,还出现了主电路和子电路信号不通的问题,后期只能大范围重构代码,浪费了大量时间。其次递归仿真逻辑很难把控,多层子电路嵌套的时候,经常出现信号重复计算、输出结果重复叠加的 bug。另外异常优先级判断很容易粗心写错顺序,我一开始把引脚冲突放在了靠前位置,不符合题目要求的报错顺序,导致测试一直不通过。同时我忽略了子电路输出编号前缀的细节,一开始没有给子电路元件加上专属编号标记,结果主电路和子电路输出内容混淆。
三、三次作业踩过的共同的坑和解决办法
三次作业写下来,我发现有几个问题是从头到尾都在犯的,整理出来避免以后再踩:
3.1 坑1:解析文本的方法太笨,经常出错
问题:作业4一开始用substring和indexOf来切割元件名称,只能解析像“X1”这种简单的名字,遇到带括号的“A(2)1”就完全解析不了,导致元件创建失败。
解决办法:改用正则表达式来匹配所有格式的元件名,一次性就能提取出元件类型、参数和编号,不管是什么格式都能解析对。
3.2 坑2:引脚顺序搞混,结果全错
问题:作业5加了控制引脚之后,经常把控制、输入、输出引脚的顺序搞混,导致三态门、译码器这些元件完全不能用。
解决办法:给每个元件都写死一个引脚对应表,用代码固定哪个编号对应哪个功能,不用自己人工判断,从根源上避免出错。
3.3 坑3:子电路信号传不进去也传不出来
问题:作业6一开始没用到组合模式,主电路和子电路各写了一套仿真逻辑,代码又多又乱,而且子电路嵌套的时候信号根本传不进去。
解决办法:让子电路也实现Component接口,跟普通元件用一样的调用方式,仿真器递归调用计算方法,自动就能把信号传到子电路里面。
3.4 坑4:输入引脚没接全也会计算输出
问题:题目要求如果一个元件的输入引脚没全部接信号,就不输出它的结果。但我一开始没加这个判断,没接的引脚程序会默认当成0,结果算出来的都是错的。
解决办法:在判断元件是否可以计算的方法里,强制检查所有必填的输入引脚是不是都收到了信号,只要有一个没收到,就不计算也不输出。
四、后续代码怎么改进
结合三次作业遇到的问题,我觉得代码还有这几个地方可以优化:
- 把通用代码抽成工具类:把解析文本、检测错误、检查引脚这些常用的代码,单独放到几个工具类里,不要都堆在仿真器和解析器里,让核心代码更干净;
- 补上各种边界情况的处理:比如输入是空的、没有任何连线、子电路嵌套太多层、引脚编号超出范围这些极端情况,现在都没处理,很容易出问题;
- 一定要加注释:把整个代码的架构、核心的算法、每个元件的引脚规则、错误的优先级这些地方都加上注释,不然过段时间自己都看不懂;
- 加更多的错误检测:现在只检测了五种连接错误,还可以加比如元件编号重复、子电路没定义就用了这些错误,让程序更健壮。
五、学习总结和课程建议
5.1 这次学到了什么
- 熟练掌握了封装、继承、多态这三个最核心的面向对象特性,知道什么时候该用它们;
- 明白了“一个类只干一件事”、“加新功能不改旧代码”这些设计原则的重要性,能让代码越写越好维护;
- 学会了用组合模式来处理嵌套的结构,也会写简单的递归代码了;
5.2 自己的不足
- 边界意识太差,写代码的时候只想着正常情况怎么跑,根本想不到各种奇怪的极端情况,导致很多测试用例过不了;
5.3 对课程作业的建议
实验,pta习题集,实验报告多者同步,时间重叠




浙公网安备 33010602011771号