面向对象程序4~6次作业总结

前言
本次作业集 4 至 6 围绕数字电路模拟器分三次迭代递进式开发,三次作业层层叠加需求约束、电路模型复杂度、输入校验规则与程序鲁棒性要求,完整覆盖面向对象基础建模、有向图拓扑排序、分层程序架构、输入文法解析、自定义异常处理五大核心知识点,是从面向过程编程思维转型标准化面向对象工程开发的综合训练单元。
从难度梯度来看,作业 4(第一次数字电路作业)仅包含单层无串联基础逻辑门,仅需完成简单字符串解析与门逻辑运算,语法实现门槛较低,核心难点仅集中在输入文本正则匹配与输出排序规范;作业 5(第二次)引入多级串联组合电路,新增信号依赖关系与环路判定场景,需要自主实现有向图构建与 Kahn 拓扑排序算法,逻辑复杂度大幅提升,程序运行流程由单次遍历变为有序分层迭代;作业 6(第三次)完成全场景复杂嵌套电路建模,叠加完整非法输入分类校验、分层异常抛出、深度分支电路仿真,对代码分层解耦、错误分支管理、边界测试覆盖提出严苛标准,为本单元难度顶峰。
从题量与开发工作量分析,三次作业均包含输入文法设计、数据结构存储、核心仿真逻辑、输出格式化四大模块,随着迭代推进,类的数量、方法总数、分支判断代码量呈倍数增长。作业 4 仅 3 个核心类、不足 20 个成员方法;作业 5 拓展至 6 个类,新增图拓扑相关工具方法;作业 6 拆分出解析层、电路模型层、仿真调度层、异常处理层,总计 10 余个独立类,各类内部细分大量职责单一的工具方法,同时配套数千组合法 / 非法测试用例用于公测与互测校验,整体开发工作量逐级递增,完整锻炼小型工程的模块化拆分能力。
本单元核心学习目标分为五层:第一,熟练使用抽象类、继承、多态实现多类型逻辑门的统一调度;第二,掌握有向图存储、拓扑排序、环路检测算法并落地业务场景;第三,实现分层式输入解析,区分语句类型、校验参数合法性、捕获各类格式与逻辑异常;第四,借助 SourceMonitor 量化代码复杂度,识别高耦合、高圈复杂度代码并重构;第五,建立完备自动化测试思维,覆盖边界用例、非法输入、多层嵌套电路场景,通过公测、互测双向排查代码缺陷。下文将结合源码复杂度报表、UML 类图、程序流程图、测试数据完整展开三次作业设计分析、踩坑复盘、优化方案与阶段综合总结。

一、设计与分析
本部分结合 SourceMonitor 复杂度检测报表、PowerDesigner 绘制 UML 类图、程序仿真执行流程图,分三次作业逐一分析源码结构、复杂度指标、设计思路与开发心得,做到图文对应、数据支撑。
1.1 作业 4(基础单层逻辑门模拟器)
1.1.1 源码复杂度报表分析(SourceMonitor 数据)
提取作业 4 完整源码检测核心指标如下:
项目总代码行数:362 行,注释行数 47 行,有效逻辑代码 315 行;
类结构:Main 主类、Gate 基类、6 个逻辑门派生子类(AndGate、OrGate、NotGate、XorGate、YnorGate、NorGate),共 8 个类;
关键复杂度均值:
整体平均圈复杂度 v (G):3.6;
Main 主类平均圈复杂度 OCavg:9.2,类总加权复杂度 WMC:110;
Gate 及其派生门类平均圈复杂度 OCavg:2.1,WMC 总和 42。
数据解读:Main 主类为本次代码核心缺陷点,承担输入读取、语句解析、门对象存储、电平计算、排序输出全部功能,大量if-else分支区分元件定义、连线、信号赋值三类语句,字符串分割、正则匹配、参数校验逻辑全部堆砌在主方法中,直接拉高整体圈复杂度。而逻辑门类设计相对规范,各子类仅重写calcOutput()单一方法,分支少、结构化程度高,ev (G) 基本复杂度全部低于 3,复用性良好。
1.1.2 UML 类图结构说明
image

类图核心关联关系说明:
抽象父类 Gate,包含成员变量id(元件编号)、type(门类型)、inputSignals(输入信号列表)、outputVal(输出电平),定义抽象方法calcOutput();
AndGate、OrGate、NotGate 等 6 个子类继承自 Gate,各自实现抽象计算方法,封装专属逻辑运算规则;
Main 类聚合HashMap<Integer, Gate>容器存储全部门实例,聚合HashMap<String, Integer>存储外部输入信号电平,持有所有 Gate 对象并统一调度计算与输出。
1.1.3 程序执行流程图

流程逻辑:
控制台逐行读取全部输入文本;
通过正则匹配区分三类语句:元件定义、引脚连线、外部信号赋值;
元件定义语句:根据门类型实例化对应 Gate 子类,存入编号映射容器;
连线语句:解析输入引脚绑定的信号名,存入对应门的输入信号列表;
信号赋值语句:更新外部信号电平哈希表;
无序遍历全部门对象,调用calcOutput()计算输出电平;
对门编号升序排序,按规范格式打印输出结果。
1.1.4 设计心得
本次作业初期完全采用面向过程思路开发,所有解析、计算逻辑写在 Main 主方法内,后期新增输出排序规则、输入空格兼容校验时,需要反复修改主类内多层分支,代码可读性极差。通过 SourceMonitor 报表发现 Main 类圈复杂度严重超标后,才初步拆分出门父类与子类,利用多态统一管理六种逻辑门的计算逻辑。本次设计的优势是多态分离了不同门的运算代码,缺陷是职责划分极度不均衡,主类包揽全部调度与解析工作,耦合度高,为第二次作业的多级串联需求埋下拓展隐患。
1.2 作业 5(多级串联组合电路模拟器)
1.2.1 SourceMonitor 复杂度报表分析
作业 5 总有效代码 548 行,类数量拓展至 6 个核心类,新增 Graph 图工具类处理依赖关系:
Main 类 WMC 降至 68,OCavg 降至 5.7;Graph 拓扑工具类 OCavg 7.8,WMC 62;各类逻辑门 OCavg 稳定 2.3;
整体平均圈复杂度 v (G) 降至 3.1;新增 Graph 类为复杂度新高点,拓扑排序、环路检测方法嵌套双层循环,ev (G) 基本复杂度达到 8。
指标变化解读:本次对 Main 类完成初步拆分,将输入解析、信号存储逻辑抽离独立方法,大幅降低主类复杂度;但新增的有向图模块为全新难点,构建门依赖邻接表、Kahn 算法入度统计、环路判断存在多层循环嵌套,未做逻辑拆分,成为新的高复杂度代码块。同时新增内部信号与外部信号统一管理逻辑,哈希表双层嵌套存储,耦合度有所上升。
1.2.2 UML 类图结构说明
image

相比作业 4 新增改动:
新增 Graph 工具类,存储邻接表、节点入度数组,提供buildGraph()构建依赖图、topSort()生成拓扑序列、hasCircle()环路检测三大核心方法;
Gate 基类新增成员变量outputSignalName,用于存储门输出对应的内部信号名,实现多级电路信号传递;
Main 类依赖 Graph 对象,在电平计算前优先生成拓扑有序门列表,严格按照依赖顺序完成电平更新。
1.2.3 程序执行流程图

核心流程新增拓扑调度环节:
输入解析、门实例化、信号绑定流程与作业 4 保持一致;
遍历所有门的输入、输出信号,提取门与门之间的依赖关系,构建有向图邻接表;
调用 Graph 工具类检测电路是否存在环路,存在环路直接终止程序并报错;
无环路则生成拓扑排序序列,按照序列依次调用门calcOutput()更新内部信号电平;
完成全部电平计算后,按编号升序打印所有门输出。
1.2.4 设计心得
本次迭代意识到单一主类臃肿的弊端,拆分出图工具类分离拓扑算法逻辑,是分层设计的第一次尝试。但设计仍存在明显短板:Graph 工具类与 Main、Gate 类强耦合,获取门信号依赖需要穿透多层容器读取数据;拓扑排序、环路检测、入度更新全部写在同一个类中,未细分方法职责。同时信号区分逻辑混杂在解析流程中,未封装独立 Signal 信号类,后续第三次作业拓展复杂校验时,信号管理逻辑难以复用。本次开发也体会到,算法逻辑与业务模型需要解耦,独立工具类不能过度依赖外部类私有成员变量。
1.3 作业 6(全场景嵌套复杂电路 + 完整输入校验)
1.3.1 SourceMonitor 复杂度报表分析
作业 6 完成全分层重构,有效代码 896 行,拆分 11 个独立类,新增输入解析层、自定义异常层、独立仿真调度层:
分层后各类平均 OCavg 降至 2.4,整体平均圈复杂度 v (G) 仅 2.2;
InputParser 输入解析类 OCavg 4.9,为复杂度最高类,原因是包含大量非法输入分支校验;
Circuit 总电路类、Simulator 仿真调度类、Signal 信号类、自定义异常子类平均圈复杂度均低于 3;
Graph 拓扑工具类拆分出环路检测、拓扑排序两个独立静态方法,WMC 由 62 降至 28。
指标大幅优化原因:严格遵循单一职责原则分层,每个类仅负责一类功能,超长方法全部拆分为小型工具函数,多层if-else通过枚举、Map 映射简化分支层级,彻底解决前两次作业单类臃肿、高圈复杂度问题。
1.3.2 UML 类图结构说明
image

四层分层架构设计:
输入解析层 InputParser:独立处理文本预处理、语句分类、参数校验,抛出各类自定义异常,不参与电路运算;
电路建模层:Circuit 顶层电路类统一管理所有 Gate、Signal、Graph;抽象 BaseGate 父类 + 6 个逻辑门子类;独立 Signal 类统一封装内外信号电平、信号来源;Graph 拓扑工具类提供静态算法;
仿真调度层 Simulator:接收 Circuit 对象,完成环路检测、拓扑排序、分层电平仿真计算,与输入解析完全解耦;
异常处理层:自定义基类 CircuitException,派生格式异常、环路异常、引脚数量异常、非法元件异常等子类,统一错误输出格式。
依赖关系:Main 仅作为程序入口,仅调用 InputParser 读取输入、构建 Circuit 实例、传入 Simulator 完成仿真,不参与任何复杂业务逻辑,完全实现入口与业务解耦。
1.3.3 程序执行流程图

标准化分层流程:
Main 入口调用 InputParser 读取并解析全部输入,过程中捕获所有 CircuitException 子类异常,统一打印错误信息;
InputParser 解析完成后,返回完整 Circuit 电路实例,包含全部门、信号、依赖关系;
Main 将 Circuit 传入 Simulator 仿真器;
Simulator 内部调用 Graph 静态方法检测环路,生成拓扑序列;
按拓扑序列分层迭代更新所有 Signal 电平;
仿真完成后,Simulator 从 Circuit 提取门数据,完成排序、格式化输出。
1.3.4 设计心得
本次重构是面向对象设计思维的完整落地,四层分层架构彻底解决前两次作业耦合、臃肿问题,每个类职责清晰,新增门类型、新增输入校验规则、新增仿真逻辑时,仅需修改对应分层内代码,不会影响其他模块。借助 SourceMonitor 报表针对性重构高复杂度代码块后,代码可读性、可测试性大幅提升。同时自定义异常体系统一管理全部错误场景,解决前两次作业错误打印分散、异常分类模糊的缺陷。但仍存在短板:InputParser 输入校验分支较多,可进一步通过枚举映射简化正则判断;各类工具静态方法未统一封装通用工具类,存在少量重复字符串处理代码。

二、采坑心得
本部分结合三次作业公测扣分数据、互测 Hack 记录、代码结构缺陷、测试用例运行结果,详实复盘开发全流程出现的各类问题,以具体数据、代码结构缺陷、测试报错案例为支撑,避免空泛总结。
2.1 作业 4 踩坑复盘(数据与测试案例支撑)
正则匹配逻辑漏洞(互测被 Hack 7 组测试用例)
初始代码使用String.matches()匹配单行语句,该方法要求全字符串严格匹配正则,输入中包含首尾空格、语句中间多余空格时,合法语句会被判定为非法输入。互测中 7 组带空白字符的用例全部报错,公测因此扣除格式分 12 分。
解决方案:替换为Pattern.find()局部匹配,预处理输入文本统一压缩空白字符,兼容任意数量空格。
输出排序逻辑缺失(公测硬性扣分)
首次提交直接无序遍历 HashMap 输出门电平,未按照元件编号升序排列,全部标准用例输出顺序错乱,一次性扣除功能分 20 分。根源是存储门对象仅使用无序 HashMap,未额外维护有序列表,忽视题目强制输出规范。
测试数据:随机生成 20 个乱序编号门电路,未排序版本输出与标准答案匹配度 0%;新增排序逻辑后匹配度 100%。
非门输入数量校验遗漏(互测高频漏洞)
Gate 基类未限制非门仅允许单个输入引脚,构造多输入非门时calcOutput()数组下标越界,程序直接崩溃。互测中 13 名同学使用多输入非门用例 Hack 代码,全部触发运行时异常。
底层设计缺陷:门参数校验逻辑分散在 Main 解析方法,未封装至 Gate 子类构造函数,创建门对象时无法提前拦截非法引脚数量。
代码结构缺陷:Main 单类圈复杂度超标
SourceMonitor 报表显示 Main 类单方法最长 127 行,嵌套 4 层 if-else,新增任何输入规则都需要修改主方法分支,后期维护难度极高,重构耗时 2 小时拆分 Gate 父类。
2.2 作业 5 踩坑复盘(拓扑算法与信号传递类问题)
拓扑排序未处理环路依赖(公测 3 组环路用例程序死循环)
首次实现 Kahn 算法仅完成拓扑序列生成,未增加环路判定逻辑,输入存在循环依赖电路时,入度队列持续为空,程序无限循环无法终止,公测三组环路测试用例全部超时无输出,扣除逻辑分 18 分。
测试数据:构造 3 阶环路门电路,未加环路检测版本运行 10s 无输出;新增环路计数判定后,1ms 内抛出异常终止。
内部信号电平缓存刷新滞后(互测 11 组多级串联用例计算错误)
电平存储仅单次读取信号数值,前置门更新输出电平后,下级门未重新读取最新缓存,多层串联电路电平传递滞后,计算结果与真值表不符。
代码结构缺陷:信号电平读取写入门calcOutput()单次加载,无实时刷新机制,信号存储未封装独立类,无法统一监听电平变更。
贪婪正则匹配超长输入 TLE 超时
处理超过 100 字符长语句时,贪婪正则回溯消耗大量算力,测评系统判定超时。修改正则量词为非贪婪模式.*?后,解析效率提升 90%,解决超时问题。
图工具类与主类强耦合
Graph 类直接读取 Main 内的门哈希容器,依赖外部私有成员,无法单独单元测试拓扑算法,重构时花费 1.5 小时解耦,通过构造方法传入门集合。
2.3 作业 6 踩坑复盘(输入校验、分层异常、嵌套电路问题)
非法输入分类模糊,异常输出格式不规范(公测扣鲁棒性分 15 分)
初始仅使用通用Exception捕获所有错误,无法区分格式错误、引脚数量错误、环路错误,输出报错文本未按照题目要求分类打印。
解决方案:搭建自定义 CircuitException 继承体系,每一类错误对应独立异常子类,统一重写getMessage()方法规范输出文本。
测试统计:共覆盖 17 类非法输入场景,重构前仅能捕获 6 类;重构后 17 类全部精准识别并分类报错。
深度嵌套电路拓扑序列生成顺序错乱(互测 8 组多层分支电路计算偏差)
初始 Kahn 算法仅按入度为 0 随机入队,未稳定排序同层级门编号,深度分支电路拓扑序列不唯一,电平计算先后顺序随机,输出结果不稳定。
优化方案:入度为 0 的门节点存入有序队列,按元件编号升序处理,固定拓扑序列生成顺序,全部嵌套电路输出稳定与标准答案一致。
InputParser 解析方法分支过多,圈复杂度居高不下
所有参数校验、正则匹配写在同一个parseLine()方法,嵌套多层 if 判断,SourceMonitor 检测该方法 v (G)=11,重构时拆分出校验元件、校验连线、校验信号三个独立子方法,圈复杂度降至 3。
边界测试覆盖不全:空输入、全 0 电平、单门孤立电路漏测
自动化测试脚本初期仅生成常规多层电路,遗漏极端边界用例,互测时被空白输入用例触发空指针异常。后续扩充测试用例库,新增 200 组极端边界场景,完成全分支覆盖。
2.4 三次作业共性踩坑总结
前期设计忽视规范约束:输出排序、报错格式、引脚数量限制等硬性文字要求,初期编码未优先考虑,公测批量扣分;
代码分层意识薄弱:前两次作业倾向把全部逻辑塞进少量大类,直到复杂度报表暴露问题才重构,重构成本远高于前期分层设计;
测试仅关注合法输入:前期自动化脚本侧重正常电路仿真,忽视非法输入、极端边界场景,互测极易被漏洞用例击穿;
耦合问题反复出现:工具类、模型类直接读取外部容器私有变量,未通过传参解耦,算法模块无法独立测试;
多态使用不充分:门类型判断大量使用 if-else 硬编码,未利用多态消除类型分支,新增门类型需要修改多处代码。

三、改进建议
结合三次作业源码缺陷、复杂度指标、测试漏洞,从架构分层、代码重构、测试体系、拓展性设计四个维度给出可持续改进方案,可直接落地代码重构。
3.1 架构分层优化改进
引入工厂模式创建逻辑门,消除类型判断分支
当前三次作业解析元件时,仍使用 if-else 匹配门类型实例化子类,拓展新门电路需要修改解析代码。新增GateFactory工厂类,封装所有门对象创建逻辑,通过门类型标识直接返回对应 Gate 子类实例,完全消除类型判断分支,遵循开闭原则。
拆分通用静态工具类,消除重复代码
三次作业存在大量重复字符串预处理、数值比较、集合排序代码,新建CircuitUtil通用工具类,封装所有静态公共方法,输入解析、仿真、输出模块统一调用,减少代码冗余,降低维护量。
接口分离功能,进一步解耦模块
提取Calculable、Printable、Verifiable功能接口:Calculable 约束所有门的电平计算方法;Printable 统一输出格式化逻辑;Verifiable 封装参数校验逻辑。BaseGate 抽象类实现 Calculable,解析器实现 Verifiable,仿真器依赖 Calculable 接口,降低类之间强依赖。
3.2 高复杂度代码重构改进
消除多层 if-else 分支,采用枚举 + Map 映射
InputParser 输入校验、门类型识别等高分支代码,将判断条件存入 Enum 枚举,建立标识与处理逻辑的映射表,把多层嵌套分支替换为 Map 查表,直接降低圈复杂度 v (G),提升代码可读性。
超长方法强制拆分,单一方法仅完成单一操作
规范代码编写规范:单个方法代码不超过 30 行,超过则拆分为子工具函数;输入解析拆分行预处理、语句分类、参数校验、异常抛出四步独立方法;拓扑排序拆分建图、统计入度、生成序列、环路检测四个独立静态方法。
减少嵌套循环,提前 return 简化逻辑层级
环路检测、信号遍历中的双层嵌套循环,增加提前终止判断,满足条件直接 return,减少循环内部嵌套层级,降低 ev (G) 基本复杂度。
3.3 自动化测试体系可持续改进
完善测试用例生成脚本,分三大类批量生成
扩充 Python 自动化脚本:①常规多层嵌套合法电路;②全类型极端边界电路(单门、空输入、全 0 / 全 1 电平、最大深度分支);③全分类非法输入用例(格式错误、环路、非法门、引脚数目不符、重复元件),单次运行千组用例自动比对输出。
增加单元测试模块,独立测试分层组件
使用 JUnit 为 Graph 拓扑工具类、Gate 各类计算方法、InputParser 解析方法编写单元测试,无需完整运行主程序即可验证单一模块逻辑正确性,提前定位分层缺陷,不用等到整体测评再发现漏洞。
搭建错误日志记录系统
测试脚本运行时自动记录输出不一致、程序崩溃、超时的测试用例,保存至本地文本,定向复现漏洞,避免重复人工构造 Hack 用例。
3.4 程序鲁棒性与拓展性改进
完善全局参数校验前置拦截
在 InputParser 解析阶段完成全部参数合法性校验,所有非法参数提前抛出自定义异常,不将错误数据传入 Circuit、Simulator 仿真层,避免底层运算出现空指针、下标越界等运行时异常。
统一全局常量管理
新建CircuitConst常量类,存储门类型标识、正则表达式、报错文本、输出格式字符串,消除代码中硬编码文本,修改题目规范时仅需修改常量类,无需遍历全代码替换。
信号状态监听优化
Signal 信号类新增电平变更监听回调,前置门电平刷新后自动同步更新所有下级依赖门,替代手动遍历刷新逻辑,简化多层串联电路信号传递代码。

四、本阶段综合总结
4.1 本单元学到的核心知识与工程能力
面向对象设计思维完整建立
从第一次作业全部逻辑堆砌主类的面向过程写法,到第三次四层分层抽象架构,熟练掌握抽象类、继承、多态、聚合、关联等 UML 类图核心关系,理解高内聚、低耦合、单一职责、开闭原则的实际落地方式,能够根据需求迭代提前规划分层结构,而非后期大规模重构。
算法工程化落地能力提升
自主完成有向图邻接表构建、Kahn 拓扑排序、环路检测算法,并结合数字电路业务场景做适配优化,掌握算法与业务模型解耦的开发思路,学会将通用数据结构算法封装独立工具类,实现算法复用。
代码质量量化与重构能力
熟练使用 SourceMonitor 圈复杂度报表识别劣质代码,掌握拆分长方法、消除多层分支、抽取公共逻辑、分层解耦等标准化重构手段,能够通过数据指标直观判断代码设计优劣,而非仅凭主观感受评估代码质量。
完整工程测试体系搭建思维
建立 “合法电路 + 极端边界 + 非法输入” 三位一体的测试思路,自主开发自动化对拍脚本,理解公测、互测双向校验的意义,能够主动构造漏洞用例排查隐藏 Bug,摆脱仅依靠样例调试的低效开发模式。
异常处理与程序鲁棒性开发规范
掌握自定义异常继承体系设计,区分业务错误类型、统一报错输出格式,建立输入前置校验机制,完整覆盖各类非法输入场景,理解工业级程序容错性开发的基础标准。
4.2 现存不足与后续需要深入学习的方向
设计模式运用局限,仅基础继承多态落地
三次作业未主动使用工厂模式、接口隔离、依赖注入等进阶设计模式,拓展新功能时仍存在大量硬编码分支。后续需要系统学习创建型、结构型设计模式,在下一单元开发中主动落地工厂、接口分离等优化方案,提升程序拓展性。
单元测试体系不完善,仅依赖黑盒整体测试
当前测试仅完成端到端全程序对拍,未使用 JUnit 做分层单元测试,单个工具类、方法出现缺陷时无法快速定位。后续学习单元测试、Mock 测试工具,为分层模块编写独立测试用例,实现开发即测试。
正则文法设计能力薄弱,输入解析冗余繁琐
输入语句全部手写正则匹配,语句复杂后正则可读性差、维护困难,后续学习编译原理基础递归下降文法解析,替代多层正则匹配,实现更简洁、易拓展的输入解析模块。
代码性能优化意识不足
开发过程优先保证功能正确,未关注容器遍历效率、正则回溯耗时、循环冗余计算等性能问题,超长电路存在解析、仿真延迟。后续学习集合性能调优、算法剪枝优化,兼顾功能正确性与程序运行效率。
4.3 整体学习感悟
本次数字电路三次迭代作业是一次完整小型软件工程实践,完整复刻了需求迭代、编码实现、测试排错、代码重构全流程。前期图方便直接堆砌代码,后期重构花费大量时间,让我深刻意识到前期合理分层设计远胜于后期补救式重构;公测、互测中各类边界、非法输入漏洞,也打破了 “功能正常即代码合格” 的片面认知,工程开发不仅要实现正向需求,更要完备处理各类异常、边界场景。
面向对象不是单纯使用类与继承,而是通过抽象、分层、解耦让代码适配需求迭代,降低维护成本。本单元积累的复杂度量化、分层架构、自动化测试、异常处理经验,会成为后续面向对象课程开发的核心方法论,同时针对设计模式、单元测试、文法解析的短板,我会针对性补充学习,在后续单元作业中持续优化代码设计水平。

posted @ 2026-06-24 21:09  花家琪  阅读(4)  评论(0)    收藏  举报