基于数字电路模拟程序的面向对象程序设计迭代复盘总结
一、前言
这学期面向对象程序设计课程的三次迭代式作业,围绕“数字电路模拟程序”场景,让我从基础的逻辑门元件开始,逐步搭建起一个功能完善的电路模拟系统。整个过程慢慢迭代开发,每一次作业都在上一次的基础上叠加新的需求,让我们不断调整代码结构、优化设计思路,也让我对面向对象的思想有了从理论到实践的落地体会。
回顾这三次作业,我理解了很多在课本上看得懂,自己写不出,还经常容易混淆的知识点:
-
作业1:核心是理解基础逻辑门和单一职责原则。 不能把所有的逻辑都堆在一个类里,要把不同的功能拆分给不同的类,让每个类只负责自己的部分。在刚开始写代码时,我很习惯一个类包含多种方法,很多不同的业务逻辑放在一个类当中,这次让我打破了原有的面向过程思维。
-
作业2:开始深入面向对象的类间关系和复杂元件设计。 引入了三态门、译码器、数据选择器、数据分配器等更复杂的元件,还有控制引脚的概念。我学到了类和类之间不是随便调用的,它们的关系是有区别的,特别是继承和多态的运用,让代码更加灵活。
-
作业3:引入了子电路和异常检测。 这让我深刻体会到,组合模式在处理复杂结构时的强大之处,以及异常处理在实际项目中的重要性。数据校验和错误检测直接决定了系统的健壮性。
从题量和难度的变化来看,也能很明显地感受到迭代的处理问题。最直观的体现就是我的源码行数:第一次作业的时候,我只写了大概200行代码就能完成所有的功能,当时觉得也不过如此;到了第二次作业,代码到了400多行,因为要处理新的元件类型和引脚类型,这时候已经开始感觉有些类如果设计不好就会显得非常臃肿,逻辑很混乱,不好处理问题;第四次作业则直接到了600多行,不仅要加子电路,还要处理各种异常情况,代码结构的设计显得尤为重要。
为了更清晰地跟踪这三次迭代的代码质量变化,我使用了代码质量分析工具 SourceMonitor,还有 ProcessOn 来画类图,把整个过程的变化都量化了出来。这篇博客就是对这三次作业的完整复盘,讲讲我做了什么,踩了什么坑,还有学到了什么。
二、设计与分析
整个三次迭代的设计,其实就是一个不断拆分、不断优化的过程,从最开始的几个简单的类,慢慢拆成了职责清晰的多个类,每一次迭代都在调整结构,让代码更符合面向对象的设计原则。
1. 迭代一:基础逻辑门元件构成电路
第一次作业是整个系统的起点,需求是模拟五种基础逻辑门:与门、或门、非门、异或门、同或门。为了符合单一职责,我的思路就是不能把所有东西都写在主类里,要把功能拆开来,每个元件负责自己的计算逻辑。
我设计了以下类:
- LogicGate(抽象基类):所有逻辑门的基类,定义了引脚管理和计算输出的接口。
- AndGate、OrGate、NotGate、XorGate、XnorGate:具体的逻辑门类,各自实现自己的计算逻辑。
- Pin:引脚类,管理引脚的输入输出状态。
- Circuit:主协调类,负责把各个元件串起来,处理用户的输入,建立连接关系,然后调用计算。
- InputHandler:输入处理类,专门负责解析用户的输入格式。
- OutputHandler:输出处理类,负责把最终的结果按照题目要求的格式输出。
这种设计很好地体现了单一职责原则,每个类都只负责自己的事情,输入处理、输出处理、元件计算、电路协调各司其职。

2. 迭代二:多输入输出的组合电路元件
第二次作业,要求支持更多的元件类型,包括三态门、译码器、数据选择器、数据分配器,还有控制引脚的概念。元件引脚类型除了输入、输出之外,增加了控制引脚。
这次我新增和修改了以下类:
- TriStateGate、Decoder、DataSelector、DataDistributor:新增的元件类,各自实现自己的复杂计算逻辑。
- PinType:枚举类型,区分控制引脚、输入引脚、输出引脚。
- 修改了LogicGate基类:增加了对控制引脚的支持,以及对多输出引脚元件的支持(如译码器、数据分配器)。
这时我深入研究了继承和多态的运用:通过继承LogicGate基类,所有的元件都有统一的接口,但各自实现自己的计算逻辑。这让我在Circuit类中可以统一处理所有元件,不需要关心具体是哪种元件,大大简化了代码。
特别是译码器和数据分配器这种多输出引脚的元件,让我对类的设计有了更深的理解,如何在统一的接口下处理不同的输出方式。

3. 迭代三:子电路和异常输入检测
第三次作业是三次里最复杂的,不仅要处理子电路的定义和引用,还要处理各种异常输入情况的检测。
新增类和修改:
- SubCircuit:子电路类,实现了组合模式,可以像普通元件一样被引用。
- Component(抽象基类):重新设计了基类,让LogicGate和SubCircuit都继承自它,实现组合模式。
- ExceptionDetector:异常检测类,专门负责检测各种异常输入情况。
- 修改了InputHandler:增加了对子电路定义的解析支持。
这次迭代后,我对组合模式有了深刻的理解。将子电路和电路元件作为抽象的元件类的子类,可以将子电路看作一种组合型的电路元件,这样主电路就不需要关心引用的是子电路还是普通元件,统一处理即可。
在异常检测这块,我的做法是,在解析每一条连接信息时,按优先级依次检查各种异常情况,一旦发现就立即输出并停止处理,体现了防御式编程的思想。

4. 代码质量:SourceMonitor 的分析

为了更直观地看到这三次迭代的代码质量变化,我用 SourceMonitor 工具对三次提交的源码做了量化分析。虽然代码量从第一次的200多行增加到第三次的600多行,但平均圈复杂度反而降低了,最大圈复杂度也始终控制在合理范围内。
这说明在迭代的过程中,我没有把代码越写越乱,而是通过不断的拆分类和方法,把原来的复杂逻辑拆成了小的、简单的逻辑,这也是单一职责原则和继承多态带来的好处。
比如第一次作业的时候,Circuit类里有输入解析、连接建立、计算输出的逻辑,一个方法里有很多行代码,圈复杂度也比较高。到了第二次,我把输入解析拆到了InputHandler,输出拆到了OutputHandler。到了第三次,又把异常检测拆到了ExceptionDetector,最后每个方法都只有几行或者十几行代码,逻辑非常清晰,所以复杂度就降下来了。
三、踩坑心得
这三次作业,我踩了不少坑,很多都是看起来很小的细节,但是排查起来花了很多时间,现在回头看,这些坑其实都是很好的学习机会,让我明白了很多编码里的细节问题。
1. 迭代一:元件识别和引脚连接的坑
第一次作业里,我卡的最久的就是元件识别的问题。题目中与门、或门的格式是"A(2)1",而非门、异或门、同或门的格式是"X1",一开始我正则表达式写得不对,导致元件解析总是出错。后来我仔细分析了格式,写了两个不同的正则表达式分别处理这两种情况,才解决了问题。
还有一个就是引脚连接的拓扑排序问题。电路中的元件可能有依赖关系,A元件的输出连接到B元件的输入,那么必须先计算A再计算B。一开始我没有考虑这个,直接按输入顺序计算,导致结果错误。后来我实现了拓扑排序,按照依赖关系的顺序计算元件,才得到了正确的结果。
2. 迭代二:引脚类型和多输出元件的坑
第二次作业的坑主要在引脚类型的排序和多输出元件的输出格式上。题目中说"含控制引脚的元件按控制-输入-输出的顺序排序",一开始我没有仔细看这个要求,引脚编号分配错了,导致测试点通不过。
还有译码器的输出格式,不是输出每个引脚的电平,而是输出哪个引脚输出0,这个一开始我理解错了,输出格式不对。数据分配器的无效状态要输出"-",这个细节也很容易忽略。
三态门的高阻态处理也让我头疼了一阵,如何表示无效状态,以及在什么情况下忽略元件输出,这些都需要仔细处理。
3. 迭代三:子电路和异常优先级的坑
第三次作业的坑最多,因为细节太多了,稍微漏一点就错了。
第一个坑就是子电路的组合模式实现。一开始我没有设计好抽象基类,导致子电路和普通元件无法统一处理。后来我重新设计了Component基类,让LogicGate和SubCircuit都继承自它,才解决了这个问题。
第二个坑就是异常优先级的问题。题目中说"如果一条输入出现了多种异常,按以上异常先后顺序为优先级",一开始我是检测到一个异常就输出,但后来发现需要按优先级检查,即使后面检测到更高优先级的异常,也要覆盖前面的。这个逻辑一开始写反了,调试了很久才发现。
还有子电路元件输出时要带上子电路编号,这个细节也很容易漏掉,导致输出格式不对。
四、改进建议
1. 代码复用:更好地运用设计模式
现在的代码里,虽然运用了继承和多态,但还可以更好地运用设计模式。比如,各种元件的计算逻辑其实可以用策略模式来封装,这样添加新的元件类型时,只需要添加新的策略类,不需要修改现有的代码。
还有异常检测部分,现在是用if-else依次检查,可以用责任链模式来重构,每个异常检测器只负责检查一种异常,然后组成一条链,这样代码更加灵活,添加新的异常类型也更方便。
2. 质量优化:降低圈复杂度,完善注释
从 SourceMonitor 的分析结果来看,还有两个可以优化的地方:一个是圈复杂度,现在异常检测和子电路处理的方法,圈复杂度还有点高,因为有很多 if 判断,我可以把这些方法拆成更小的方法;另一个就是注释,现在的注释率太低了,我也意识到要写好注释,不然过段时间自己都看不懂了,而且如果别人要看我的代码,没有注释也很难理解。
五、总结
1. 阶段学习收获
这三次作业下来,我学到的东西很多,比上课听老师讲理论要深刻的多。
首先,我真正理解了面向对象的三大特性:封装、继承、多态。在第一次作业中,我体会到了封装的重要性,把数据和方法封装在类中;在第二次作业中,我运用了继承和多态,让代码更加灵活;在第三次作业中,我运用了组合模式,体会到了设计模式的强大。
然后,我明白了数据校验和异常处理的重要性,用户的输入不是永远正确的,你必须在最开始就把非法的输入检测出来,并给出清晰的提示,不然程序很容易崩溃,而且排查起来很难。
还有,我学会了用工具来分析代码质量,比如 SourceMonitor,很清晰看出代码的质量是可以量化的,可以通过复杂度、注释率这些指标来衡量,也可以将几次的代码对比比较得出自己的方向是否错误,是否走偏,这对我以后写代码帮助很大。
2. 后续研究方向
接下来,我还想深入学习一下更多的设计模式,现在的代码虽然运用了组合模式,但还有很多设计模式可以学习,比如策略模式、责任链模式、观察者模式等等。如果将来要处理更复杂的电路模拟,这些设计模式都会派上用场。
我还想研究一下时序电路的模拟,这涉及到状态的保存和时钟信号的处理,会更有挑战性。
3. 课程优化建议
这三次迭代式的作业设计的很好,让我体会到了真实项目的开发过程,不过我也有一点小小的建议:
我觉得现在的作业,前后的衔接还可以再加强一点,现在每次作业的需求变动,我几乎要重新写大部分的代码,没有真正的在老代码上加新功能。比如第一次的代码,到第二次的时候,很多都要重构,第三次又要重新设计基类,这样没有真正体会到迭代开发中"在原有代码基础上增量开发"的过程。
我希望以后的作业,需求是逐步叠加的,第一次做了基础逻辑门,第二次就在这个基础上加新元件,第三次在这个基础上加时序电路,这样我们就能真正的迭代,把老的代码重构,加新的功能,而不是每次都像做一个新的项目。
还有,希望老师能给我们一些工具的使用指导,比如 SourceMonitor、PowerDesigner 这些,我一开始用的时候,摸索了好久才会用,如果有一点指导的话,我们能更快上手,也能更早的用这些工具来分析自己的代码。
最后,这几次作业虽然写的很辛苦,经常要调试很久来通过测试点,但也真的学到了很多实用的东西,这些东西比上课听的理论道理要深刻的多,更加理解了书本中的抽象的道理。

浙公网安备 33010602011771号