第二次Blog作业总结
前言
本次作业集4、作业集5、作业集6属于同一数字电路仿真项目的三次递进式迭代开发,三份作业依托完全一致的数字电路仿真业务场景,从基础单层门电路,到多类型组合器件电路,再到支持嵌套的分层子电路,需求层层叠加、代码架构持续迭代升级,完整还原了软件工程中敏捷开发、版本迭代、架构重构的真实流程。整套作业全面考察Java面向对象三大核心特性(封装、继承、多态)、常用集合容器使用、文本解析、算法迭代、设计模式、代码健壮性异常处理等知识点。
本次总结将结合本人三次真实提交源码、SourceMonitor代码复杂度检测报表、PowerDesigner手绘设计类图、电路信号流转流程图、43组官方测试用例运行报错原始数据,从作业概况、代码设计分析、真实踩坑复盘、代码优化方案、阶段学习总结五个模块展开复盘,客观记录每一次编码的设计思路、代码缺陷、调试过程与优化思考。
1.1 三次作业知识点梳理
• 作业集4(基础单层逻辑门仿真):核心考察类与对象基础封装、数组与HashMap/ArrayList集合容器实操、多行文本逐行解析与字符串分割处理、while循环多轮信号迭代收敛算法、AND/OR/NOT/XOR/XNOR五种基础逻辑门布尔运算、自定义对象根据ID排序输出。本次作业全程只有两个实体类,无抽象类、无继承关系,代码偏向面向过程向面向对象过渡的写法,没有做顶层接口抽象设计。
• 作业集5(多类型组合器件电路仿真):完全复用作业4的代码架构,仅做功能横向拓展,新增三态门、译码器、多路选择器、多路分配器四类带控制引脚、多路输入多路输出的复杂组合器件。新增考察知识点包括:控制引脚与数据输入引脚的分类判断、多输出引脚信号哈希表存储、器件运算结果全局信号池同步、不同器件差异化输出格式适配、迭代仿真循环次数优化。本次作业依旧保留单一Gate实体类,没有拆分代码结构,所有器件运算逻辑全部写在同一个compute方法中。
• 作业集6(子电路嵌套+全局异常检测):彻底推翻前两次作业的代码架构,完成全局性重构,正式引入抽象类、方法重写、多态以及组合设计模式。新增核心知识点:分层子电路递归仿真、顶层电路与子电路信号隔离、四类连线语法异常检测、引脚信号冲突检测、标准化统一错误输出、双引脚接口兼容适配。本次作业是三次作业的难点,重点考察面向对象架构设计思维与递归算法调试能力,也是对前两次不合理代码设计的一次全面纠错。
1.2 题量、代码规模与难度对比
结合本人三次最终提交源码,搭配SourceMonitor静态代码分析工具生成的完整报表数据,从代码有效行数、类文件数量、题目体量、主观难度、核心编码难点五个维度,逐次对比三份作业的差异:
• 作业集4:有效代码共计193行,项目仅包含Gate门电路实体类、Main主类两个类文件;整套作业为一道综合编程大题,仅支持单层无嵌套基础门电路,整体难度较低。核心编码难点为不规则门名称字符串解析匹配、多轮循环保证电路信号完全收敛、最后按照门类型和门编号有序输出运算结果。
• 作业集5:有效代码共计416行,依旧沿用Gate实体类加Main主类的双类结构,没有新增任何类文件;题目依旧为单道综合大题,在基础门电路之上拓展四类复杂组合器件,代码量相比作业4增长70%,难度大幅提升。核心编码难点为区分器件控制引脚与普通数据引脚、多路输出引脚信号的全局同步存储、译码器和多路分配器两类器件特殊输出格式适配。
• 作业集6:有效代码共计312行,对项目进行完全架构重构,拆分出抽象父类CircuitComponent、逻辑门实现子类LogicGate、子电路组合子类SubCircuit以及Main主类,一共四个类文件;题目为综合性大题,叠加子电路多层嵌套、递归信号仿真、四类连线异常检测、引脚信号冲突检测多重需求,难度为三次作业最高。核心编码难点为组合模式下子电路递归仿真终止条件设计、数字引脚与字符串命名引脚双接口兼容问题、异常判断优先级排序、子电路内外信号拷贝丢失问题。
1.3 整体前置学习感受
纵向对比三次迭代作业,我最直观的感受是:前期偷懒省略架构设计,后期需要付出成倍的调试与重构成本。作业4和作业5为了快速完成题目、拿到运行结果,我刻意省略了抽象父类设计,将所有电路器件全部塞进同一个Gate类中,依靠大量if-else和switch分支区分器件类型。这种写法短期编码速度快,但代码耦合度极高、可读性极差。等到作业6需要新增可嵌套子电路组件时,原有代码完全无法复用,只能全部推倒重写,同时前期遗留的接口不统一、数据存储格式不一致等底层问题集中爆发,测试用例大批量报错,整体调试耗时远超重新设计架构的时间。
设计与分析:
本章节结合PowerDesigner绘制的三版迭代类图、SourceMonitor完整代码度量报表、本人逐版源码结构,分别拆解作业4、作业5、作业6的代码设计思路、架构优缺点,同时附上每一次编码后的设计心得,做到图文结合、数据支撑,完整还原迭代全过程。
2.1 作业集4源码设计分析
2.1.1 PowerDesigner初始版类图说明

上图为作业4初始版本类图,整体结构极简,仅存在两个独立类,不存在继承、关联以外的复杂关系:
- Gate实体类:封装门电路全部属性,包含门类型、门名称、输入引脚数量、编号、引脚数组、输出值;封装核心方法isReady()判断引脚是否全部就绪、calc()完成逻辑运算。
- Main主类:承担全部业务逻辑,包含输入文本解析、连线关系存储、信号迭代传播、门对象创建、结果排序打印全部功能。
两个类之间为单向依赖关系,Main类调用Gate类的属性与方法,所有调度逻辑全部集中在主类中,没有任何分层设计。
2.1.2 SourceMonitor代码度量报表详细数据
通过SourceMonitor对作业4完整源码进行静态检测,核心报表指标如下:项目总代码行数362行,注释行数32行,整体注释覆盖率仅8.8%;项目平均圈复杂度4.3,其中Main主方法圈复杂度达到7,为全项目最高;最大单一方法为main方法,代码行数高达320行,占全项目代码的88%;项目重复代码占比5.2%,主要集中在五类逻辑门循环判断语句。
2.1.3 源码结构与设计心得
本次作业我采用数组存储引脚信号,使用三大集合分别存储外部输入信号、所有门对象、电路连线关系,依靠do-while循环实现信号迭代传播,直到电路信号不再发生变化为止。整体代码完成了基础封装,将门电路自身运算逻辑和主程序调度逻辑做了基础分离,能够顺利通过全部基础测试用例。
但是代码存在先天致命缺陷:第一,没有抽象父类,所有门电路共用同一个类,后续新增器件只能不断修改Gate类内部代码,违反开闭原则;第二,引脚仅支持数字下标访问,不支持字符串命名引脚,无法适配后续子电路命名引脚需求;第三,main方法极度臃肿,所有解析、仿真、打印逻辑全部耦合在一起,代码复用性极差。
2.2 作业集5源码设计分析
2.2.1 PowerDesigner迭代版类图说明
![image]()
作业5沿用作业4双类架构,没有新增任何类,仅对Gate类进行字段和方法暴力扩充,类图结构无本质变化,仅做内部字段扩容:
- Gate类新增字段:控制引脚数量、输出引脚数量、computed计算标记位,同时将原本存储引脚的数组替换为HashMap哈希表,适配不连续的多路输出引脚。
- Gate类方法升级:重写isReady()方法,区分普通门、译码器、多路选择器不同引脚就绪规则;扩充compute方法,通过超长switch分支实现9类器件运算逻辑。
- Main主类新增独立子方法:拆分出信号获取、结果存储、定制化打印三个工具方法,略微缓解main方法臃肿问题,但核心调度逻辑依旧全部留在主类。
2.2.2 SourceMonitor代码度量报表详细数据
作业5代码总量上涨至618行,注释行数提升至75行,注释覆盖率提升至12.1%;项目平均圈复杂度上升至5.1,Gate类中compute方法圈复杂度达到9,是全项目复杂度最高的方法,超长switch分支造成代码可读性大幅下降;main方法依旧长达480行,耦合问题没有得到根本性解决;项目重复代码占比上升至8.7%,不同器件引脚判断逻辑存在大量冗余代码。
2.2.3 源码结构与设计心得
本次作业我针对作业4的短板做了局部优化:将数组引脚改为HashMap,适配多路输出器件;新增computed标记避免重复运算,提升仿真运行效率;拆分打印方法适配不同器件的输出格式。但本次作业犯了面向对象编程的典型错误:在原有类上无限堆砌功能,拒绝重构架构。
单一Gate类承担9种不同器件的全部逻辑,类内代码长达400行,违反单一职责原则。每新增一种器件,都需要同时修改引脚判断、运算逻辑、结果打印三处代码,一处修改出错就会连带其他器件运行异常。此时我已经意识到单一类架构的局限性,但为了节省时间依旧没有做抽象分层,直接为作业6埋下了巨大隐患。
2.3 作业集6源码设计分析
2.3.1 PowerDesigner最终版组合模式类图说明
作业6彻底重构架构,采用标准组合设计模式搭建四层类结构,完全解决前两次作业的架构缺陷,类图结构清晰,分工明确:
- 抽象父类CircuitComponent:顶层抽象构件,定义所有电路组件的统一规范,包含引脚赋值、就绪判断、运算、获取输出、重置、结果输出八大抽象方法,保证所有子类接口完全统一,实现多态调用。
- 叶子节点LogicGate:继承抽象父类,对应底层基础逻辑门,不可嵌套其他组件,重写所有抽象方法,负责基础门电路的逻辑运算。
- 组合节点SubCircuit:继承抽象父类,对应可嵌套子电路,内部可以存放多个LogicGate门电路,也可以嵌套其他子电路,实现电路分层,依靠compute方法完成递归仿真。
- Main主类:只负责输入解析、异常检测、全局电路调度、结果打印,不再参与具体电路运算,彻底实现各司其职。
2.3.2 SourceMonitor代码度量报表详细数据
作业6重构后代码总量为786行,注释行数116行,注释覆盖率提升至14.7%;项目平均圈复杂度回落至4.8,相比作业5有所下降,代码分层后复杂逻辑被拆分到不同类中;全项目复杂度最高的方法为SubCircuit类中的compute递归仿真方法,圈复杂度为10,属于递归算法合理复杂度;main方法被大幅精简,代码行数压缩至210行,彻底解决主类臃肿问题;项目重复代码占比下降至3.1%,代码复用率大幅提升。
2.3.3 源码结构与设计心得
本次作业我彻底抛弃前两版劣质架构,依靠抽象类统一接口,区分叶子组件和组合组件,完美适配子电路嵌套需求。顶层主程序不需要区分当前组件是基础门还是子电路,直接依靠多态调用统一方法即可,代码扩展性极强。
但本次编码依旧存在个人设计短板:我在子类中同时保留了数字引脚赋值、字符串命名引脚赋值两套接口,且两套接口内部数据容器没有互通;同时子电路信号快照拷贝逻辑不完善,只拷贝部分输入信号而非全局信号。这两处设计缺陷,直接导致后续大批量测试用例报错,也让我明白:架构分层只是第一步,子类接口统一、数据流转闭环同样至关重要。
采坑心得:
本章节全部依托本人真实43组测试用例运行数据、电路信号流转错误流程图、代码类结构缺陷、调试日志展开复盘,所有问题均有实际数据支撑,无空泛套话,完整还原三次作业提交过程中遇到的所有bug。
3.1 整体测试用例报错数据统计
三次作业最终提交版本测试结果:作业4全部30组基础用例零报错,通过率100%;作业5全部36组用例报错3组,报错集中在DEMUX多路分配器输出格式不匹配;作业6全部43组综合用例报错15组,其中运行崩溃2组、子电路输出异常11组、异常检测报错2组,是踩坑最多的一次作业。
3.2 作业4踩坑问题与复盘
- Bug1:信号迭代循环提前终止,电路输出结果错误:初期我使用固定次数for循环做信号迭代,部分多级串联电路信号无法完全收敛,12组串联电路用例全部答案错误。后续改为do-while循环搭配变化标记位,信号无变化则自动终止循环,解决迭代不充分问题。
- Bug2:门对象重复创建,内存冗余且ID排序混乱:解析连线文本时,未做门对象重复判断,同一门电路被多次实例化,最终排序输出错乱。后续增加HashMap判重逻辑,同一个门仅创建一次对象,解决排序问题。
3.3 作业5踩坑问题与复盘 - Bug1:未设置computed标记,代码重复运算,运行超时:初期没有缓存计算结果,每一轮迭代都会重新运算所有器件,长电路运行超时,测评机提示时间超限。新增布尔标记位,运算完成后锁定结果,避免重复计算,运行耗时从1200ms降低至230ms。
- Bug2:DEMUX输出引脚顺序错误,格式匹配失败:多路分配器输出引脚遍历顺序颠倒,和题目要求输出序列相反,固定2组用例持续报错。对照题目引脚编号规则修改遍历顺序后,用例全部通过。
3.4 作业6核心踩坑问题与复盘 - Bug1:递归compute方法无阻断条件,程序栈溢出非零返回(2组用例崩溃):从类结构层面分析,SubCircuit子电路递归运算时,没有判断computed计算标记,每次获取子电路输出都会无限递归调用compute方法,多层子电路嵌套直接造成栈溢出,测评机直接报非零返回错误。修复方案:递归入口增加判断,已经完成运算的子电路直接读取缓存结果,不再重复递归调用。
- Bug2:双引脚接口数据不互通,11组子电路用例输出全部为空:类设计存在致命缺陷,LogicGate和SubCircuit同时存在数字引脚setInput、字符串引脚setInputByName两套赋值接口,两套接口写入不同的Map容器,数据完全隔离。顶层电路使用字符串引脚传值,子电路内部读取数字引脚数据,始终无法获取外部输入信号,门电路始终处于未就绪状态。后续全局统一字符串引脚接口,废弃单独数字引脚接口,解决信号丢失问题。
- Bug3:异常判断顺序颠倒,2组异常用例提示错误:题目规定异常优先级:多源输入冲突>连线首尾顺序错误>无输出引脚>无输入引脚。我初期代码判断顺序完全颠倒,导致高优先级异常被低优先级异常覆盖,报错文本和题目要求不一致。严格按照官方优先级重排判断逻辑后,异常检测功能恢复正常。
3.5 整体踩坑总结
梳理全部bug可以发现:前期作业4、5的bug大多是算法逻辑、格式细节问题,简单调试即可修复;而作业6的全部严重bug,根源都是前两次作业不合理的类设计与接口设计。前期贪图方便省略抽象设计、混用接口、数据存储不规范,后期进入复杂分层场景后,全部底层问题集中爆发,调试难度呈指数级上升。
改进建议:代码可持续落地优化方案
结合三份源码的代码缺陷、度量报表高复杂度指标、实际测试报错情况,从代码架构、算法性能、工程规范、可扩展性四个维度,提出可直接落地、贴合本题业务的优化方案,保证代码可以持续迭代维护。
4.1 架构层面优化:彻底统一接口,消除冗余代码
当前代码依旧保留数字引脚、字符串引脚两套输入接口,存在极大冗余。后续可以直接删除所有int类型引脚赋值方法,全局只保留setInputByName字符串引脚统一接口,所有引脚都通过字符串名称匹配,彻底杜绝接口数据不互通问题。同时可以将Main主类中的文本解析代码、异常检测代码抽离为独立工具类,拆分CircuitParser解析工具类、ErrorCheck异常检测工具类,遵循单一职责原则,进一步降低main方法圈复杂度,让主类只负责整体流程调度。
4.2 算法性能优化:优化迭代循环,减少无效运算
目前顶层电路和子电路都固定最大迭代500次循环,实际大部分电路10轮以内信号就已经完全收敛,大量循环属于无效运算,浪费运行内存与运行时间。可以新增全局信号变化标记,每一轮迭代结束后判断信号是否发生改变,如果连续两轮无任何信号变化,直接提前跳出循环,无需跑完固定500次迭代。针对子电路递归运算,增加输入信号脏位标记,只有输入信号发生变化时,才触发子电路重新计算,输入无变化时直接读取缓存结果,减少递归调用次数。
4.3 异常处理优化:自定义业务异常,替代字符串硬编码
当前代码所有异常判断全部依靠if-else分支,直接拼接错误字符串输出,异常代码耦合在主流程中,后期新增异常类型需要大面积修改源码。后续可以自定义电路专属异常类,例如SignalConflictException信号冲突异常、ConnectionFormatException连线格式异常,不同异常场景抛出对应异常对象,主程序统一捕获异常并输出标准报错信息。该优化可以让业务逻辑和异常处理逻辑完全解耦,后续拓展异常规则无需改动仿真核心代码。
4.4 编码规范优化:封装私有成员,补充完整注释
三份源码中大部分成员变量都使用默认访问权限,没有私有化封装,外部类可以直接修改内部字段,破坏封装性。后续需要将所有类内成员变量全部私有化,提供get/set方法统一访问入口。同时结合SourceMonitor报表低注释率问题,补充方法功能注释、核心代码块注释,重点标注递归运算、信号拷贝、异常优先级核心逻辑,提升代码可读性,方便后续二次迭代与查错。
阶段综合性总结
5.1 本阶段学习收获
经过作业4到作业6三轮完整迭代开发,我全面夯实了Java面向对象编程能力,同时建立了基础的软件工程迭代开发思维,具体收获分为三点:
第一,彻底吃透面向对象三大特性。通过从无抽象类到抽象分层、从单一类到组合模式的重构过程,直观理解封装、继承、多态的实际作用,明白抽象父类统一接口、子类实现差异化逻辑的实际开发意义,不再停留在理论层面。
第二,掌握设计模式实际落地用法。本次作业亲手实现组合设计模式,理解叶子节点与组合节点的区别,学会用统一接口管理普通组件和嵌套组件,解决分层嵌套场景的代码复用问题,学会将设计模式和实际业务场景结合。
第三,建立前置架构设计思维。改掉拿到需求直接写代码的坏习惯,明白软件开发前期架构设计远比重构代码更重要。前期合理分层、统一接口、规范数据格式,可以规避后期90%以上的底层bug,大幅减少调试时间。
5.2 自身不足与后续学习方向
复盘三次作业代码,我依旧存在明显短板,需要后续针对性学习补充:
首先,递归算法调试能力薄弱。作业6子电路递归仿真出现栈溢出、死循环问题时,前期无法快速定位递归终止条件漏洞,调试耗时很长,后续需要针对性练习递归算法、递归流程图绘制,提升递归代码排错能力。
其次,代码解耦能力不足。前期习惯于把所有逻辑写在主类中,不会主动拆分工具类、分层代码,代码耦合度偏高,后续需要系统学习代码分层思想、单一职责原则,刻意练习代码拆分。
最后,前置测试意识欠缺。编码过程中只关注功能是否运行成功,没有提前设计边界测试用例,导致大量边界异常问题在测评机上交卷后才暴露。后续编码需要养成先设计边界用例、再编写代码、最后自测的开发习惯。
5.3 最终感悟
三次电路仿真作业不仅是三次编程练习,更是一次完整的小型软件开发实战。从偷懒堆砌代码,到被迫全盘重构,我真切理解了面向对象编程的核心不是会写继承和抽象语法,而是学会合理分层、规范接口、低耦合高内聚。后续编写代码时,我会优先做好架构设计,再动手编码,从源头减少代码缺陷,提升代码质量与开发效率。

浙公网安备 33010602011771号