第四至六次作业总结

第四至六次作业总结

前言

第四到第六次作业集是又一次迭代式作业,从第四次基础的门电路构建与信号传播,到第五次多路选择器、三态门等复杂器件的引入,以及加入了控制引脚和多输出管理等特性,而最终的第六次迭代作业则引入了子电路定义、嵌套连接、引脚映射与连接合法性校验。
这三次OO作业难度和复杂度依次上升,结合了继承与多态、数据结构设计、递归解析、输入鲁棒性处理等知识点,对抽象思维与工程能力提出了较高要求。


第四次作业

这次的作业题目要求是实现一个简单的组合逻辑电路模拟器,支持与门、或门、非门、异或门、同或门五种基本门,以及输入信号的解析和元件之间的简单连接。总体来说这次作业相较于后两次迭代作业来说是比较简单的,因为门电路种类有限,并且也只有简单连接。

本次作业共设计了七个类,其之间的关系如图所示:

8df16baca1ea0a682ac8c315cb9649cd

Signal和Pin两个类仅包含其基本属性与简单的状态判断和引脚连接方法,无复杂逻辑;功能型的逻辑运算都放在了AndGate、OrGate、NotGate、XorGate、YorGate这五个门类中,它们统一继承自抽象类Gate;而Circuit类负责管理所有门实例与连接关系,Input和Output类分别负责输入解析与输出打印,主类Main则负责读取输入并协调各模块。

这其中有一个点就是在刚开始设计的时候我原本是想将所有门类的逻辑运算放在Circuit中统一处理,但因考虑这不符合多态与SRP原则,于是将每个门的逻辑运算分别封装到各自的类中,通过抽象方法run()来统一调用接口,但缺点是新增门类型时需要创建新类并修改Circuit中的creatGate方法,扩展性还不够理想。

到此,第四次作业的功能性要求基本达标,用户输入一组门定义和连接关系,以及输入信号值;程序解析连接,按顺序对各门进行刷新和运算,最终筛选出所有产生有效输出的门并按类型和编号排序输出。

接下来是对第四次作业中类与方法的复杂度分析,下面是第四次作业的生成报表内容:

443edcaedc6223f8b745f1b98fabbe26

可见其中复杂度最高的便是Input中的inputConnectLine()方法和Circuit中的creatGate()方法,主要原因在于连接关系的字符串解析需要大量条件分支,以及门类型判断采用了多个if-else,导致复杂度较高,在未来深入学习工厂模式后定能解决此等问题。


第五次作业

本次作业在第四次作业的基础上进行了迭代,在原有的五种基本门的基础上又增加了三态门、译码器、数据选择器、数据分配器四种复杂器件,门类增加了控制引脚和多输出引脚的管理,并且按照器件类型的不同采用不同的输出格式。

本次作业相较于上次新增加了TriStateGate、MGate、FGate、ZGate四个门类,并对Gate抽象类进行了重构,其之间的关系如图所示:

9f24e6b647defd37181675bffc6ec699

本次作业的业务逻辑与上一次差别较大,不再是简单的单输入单输出运算,而是引入了多控制引脚、多输出引脚的概念。现在的门类需要管理三种引脚:控制引脚controls、输入引脚inputs和输出引脚outputs,体现了更复杂的硬件抽象与面向对象的封装性。
这次的题目要求相比于上次就复杂得多,多了很多器件的内部逻辑要求,但这也有一个好处就是使我们对继承体系的设计,以及对多态的使用更加清晰明了,深化了对抽象类的理解。

但相应的,类的复杂度也有所上升,报表内容如图所示:

914596327a5309a7aeb0cbc0245bdac0

不难发现,这次作业无论是类的数量还是其内部方法的数量都有明显的提升,其中还存在在输出打印中使用了大量instanceof判断的问题。根据题目要求不同器件需要不同输出格式,我却将其全部堆在Output类中用类型判断分别处理,这么设计肯定是不好的。但在修改的过程中却无法通过大部分测试点,只能按照原有的逻辑继续进行调整。

这次的题目新增了MGate和FGate等带控制引脚的多输出器件,并且引入了evaluate()和reset()机制避免重复计算。这次作业中我已经初步认识到如何正确地利用多态来替代类型判断。


第六次作业

本次迭代在要求上新增了子电路概念,同时增加了连接线的合法性校验。这次作业是在V1的基础上迭代,比起依次迭代,这此的作业更像是V1的另一个方向的迭代。
这次迭代的难度和代码量达到了顶峰,由于需要处理嵌套的子电路,同时保持原有的顶层连接和信号传播逻辑不变,设计上不得不采用递归构建和多次遍历。类间关系变得更加复杂,关系如图:

e81c8c45c1d9aa4aa8694436e0c88fe4

本次作业的重点是子电路的递归构建与连接校验,在这次的作业中,系统需要根据输入的数据解析子电路定义、构建内部连接、映射输入输出引脚,并最终通过顶层电路的运算得到所有有效门的输出。
这次作业有一个较大的难题,就是题目中的子电路嵌套引用,形如"C1-xxx"的引脚表示方式可以引用其他子电路的输入输出引脚。在设计resolveSubPin方法时我不确定该如何处理复杂的引脚解析。首先想到的是全局遍历所有门查找,但这在嵌套层次深时效率极低。经过思考后我决定采用递归构建的方式,在构建父电路时先递归构建所有引用的子电路,但这又引出了后面的问题:可能出现循环引用导致无限递归,经多番查找才发现需要添加已构建电路的缓存判断。
第二个问题是校验连接线,对输入输出数量的判断还好,毕竟只要统计连接行中属于输入和输出的token数量就行,但在判断多个输入驱动同一引脚时,需要在连接过程中记录每个引脚的驱动源,一旦发现冲突立即报错。于是我选择使用HashMap记录已连接的引脚和其源信号,遇到重复连接时输出错误信息并终止程序。

而依据SourceMonitor生成的报表分析(如图)

652f946defc2627daf5b3fc43ca4aa39

可以看出这次作业的复杂度特别的高。这次作业的可以说是勉强过关,但依旧有很多的测试点没有通过,在不断修改的同时类的复杂度也直线上升,很多地方无法通过测试,或是答案错误,或是非零返回,只能不断添加新的类或是新的方法来弥补,而优化方面却做得很差,导致类的数量,复杂度都特别的高


坑与测试

这三次作业的调试过程让我积累了不少排查问题的经验:

第四次最隐蔽的bug是忘记在run()前调用refresh(),引脚一直是初始的NULL。这个问题在门少的时候不明显,一旦变长就暴露出来了。
第五次的多输出门引脚编号容易搞混。MGate的引脚0是控制端而不是数据输入,我在初始化时把控制引脚和数据输入引脚的编号逻辑写反了一次,导致译码器的输出完全错位。
第六次的嵌套子电路是最难调试的。有一个测试用例是C1引用C2的输出,C2又引用C1的一个输入,构成类似反馈的结构。我的递归构建逻辑一开始没有充分检查循环,导致栈溢出。
输入校验也踩了坑。一开始我只检查了“是否有多个输入”,导致连接逻辑混乱。后来专门加了一条位置校验才彻底堵住。


目标与改进

这次作业基本业务需求虽然达标,但仍然存在许多的问题与不足,在对生成的报表进行分析后发现Output类中存在大量instanceof类型判断,主要原因是将不同门类型的输出格式化逻辑全部集中在了Output类中,这影响了可读性和可扩展性;有些类的封装性不够强,比如Pin的lastPin和value可以直接被外部修改,日后将通过遵循最小暴露原则,用行为代替数据访问等方法进行改进。


总结与展望

从基本门电路到带校验的子电路系统,这三次作业呈现了一条清晰的复杂度增长曲线。我不仅在技术层面实践了继承、多态、递归解析和错误处理,更在思维层面体会到“用面向对象模拟硬件行为”。封装与抽象的边界、扩展性与复杂度的权衡、以及测试用例的系统构造,都成为宝贵的经验。

未来,我将继续深入设计模式与代码重构方法,对设计模式进行学习,如工厂模式等。以及对代码进行优化的能力,减少代码复杂度以及更深入的学习面向对象程序设计,提升对复杂系统的分解能力,力求在满足业务需求的同时,写出更清晰、更健壮、更可维护的代码。期待下一阶段的突破与成长。

posted @ 2026-06-23 17:30  毛锦润  阅读(3)  评论(0)    收藏  举报