面向对象设计与构造——第二单元总结

写在前面
OO 课程第二单元已经结束,本单元围绕多项式求导程序开展三次迭代式作业,功能逐层叠加、复杂度逐步提升。经过完整的编码、公测互测、调试优化全过程,我不仅夯实了 Java 基础语法,逐步建立起规范化的面向对象设计思维,深刻理解代码可扩展性、可维护性在迭代开发中的核心价值,还自主搭建自动化测试对拍体系,形成了 “编码 - 自测 - 排错 - 迭代优化” 完整开发流程。

下文先梳理圈复杂度相关度量指标概念,再依次从程序结构设计、复杂度分析、公测与互测复盘、Bug 溯源与改进思路四个维度,系统总结本单元三次作业的完成情况、问题短板与成长收获。

Complexity Metrics(复杂度分析前置概念)
本次分析围绕方法级圈复杂度与类整体复杂度两个维度开展,核心指标定义如下:

方法层面三大指标

1.v (G) 圈复杂度
表征程序分支路径总量,代表覆盖全部逻辑分支所需最少测试用例数量;数值越大,分支判断、循环嵌套越多,代码可读性、出错概率越高。

2.ev (G) 基本复杂度(本质复杂度)
取值区间 ([1, v(G)]),衡量代码结构化程度;数值越高,代表 goto 式嵌套、冗余分支越多,代码结构越臃肿病态,重构优先级越高。

3.iv (G) 设计复杂度
表征当前方法与其他方法的调用耦合程度;取值越大,方法依赖关系越杂乱,后期修改一处代码极易引发连锁 bug,不利于维护迭代。

下面我将从程序结构,公测、互测以及bug分析几个方面来总结我第二单元的三次作业。

第四次作业总结

作业要求
完成基础多项式输入合法性校验、多项式求导运算;多项式由若干项加减构成,项仅包含整数常数项、幂函数项。

实现方式
读取输入字符串后,使用ArrayList容器逐条存储拆分后的多项式单项;遍历每一项套用求导公式完成求导运算,最后对系数、符号、冗余数字做输出格式化处理后打印结果。

代码规模
image

类图设计
整体采用三层简单结构:
Main:程序入口,负责输入读取、整体流程调度、结果打印;
PolyList:多项式整体管理类,承担字符串解析、多项式合并、求导总逻辑、格式化输出功能;
PolyNode:单项实体类,存储单一项系数、x 指数,封装单项求导方法。
class

复杂度分析
image

1.问题集中体现在 PolyList 构造方法、print 输出方法圈复杂度偏高:两个方法内部堆砌大量多层if/else分支判断,分支路径繁多,v(G)、iv(G)指标显著偏高;
2.构造方法臃肿根源:自身正则表达式使用熟练度不足,没有拆分分步匹配逻辑,试图用巨型正则一次性匹配全部输入格式,解析逻辑全部挤压在构造函数内;
3.print 方法高复杂度原因:输出正负号、系数 1、常数项、首项符号等各类边界场景全部集中在一个方法判断,职责过于繁杂。

Bug 分析
公测情况
程序全部用例逻辑正确性通过,无功能性 Bug;仅输出格式优化细节存在疏漏:化简时只处理-x省略系数 1,遗漏-1*x场景,导致部分测试点格式失分,性能分未拿满。

互测情况
1.自身被测出 1 个 Bug:输出化简逻辑考虑不全,仅处理单零项输出,遇到0*x3+0*x4多个零项叠加场景,未清空全部零项、出现空输出异常;
2.个人 Hack 他人共计 17 次,共性问题集中在正则表达式设计缺陷:编写正则时边界场景考虑缺失,空格、首尾符号、空输入、多余运算符等非法输入校验不全,非法格式判定出现误判、漏判。

自测测试方案
1.自动造数:使用 Python xeger 库基于合法正则批量自动生成海量测试表达式;
2.结果校验:调用 Sympy 符号计算库求解标准求导结果,Java 程序运算完成后字符串比对校验等价性;
3.人工审计:通读同组同学源码定位逻辑漏洞,本次互测中有两处隐蔽 Bug 为静态读代码排查得出。

第五次作业总结
作业要求
拓展多项式结构:项由多个因子相乘构成,新增因子类型 (\sin(x)、\cos(x));依旧完成输入格式校验、多项式求导、同类项合并、三角函数化简优化。

实现方式
1.存储结构改用HashMap:自定义三元组ItemDegs作为key(存储 x 指数、sin 指数、cos 指数),value存储对应合并后系数,天然实现同类项合并;
2.求导逻辑:基于乘积求导、三角函数求导公式逐项推导;
3.化简优化:采用 DFS 遍历,利用三角恒等式完成同类三角函数合并化简。

代码规模
image

类图设计
PolyList:顶层多项式容器,负责输入解析、整体求导、合并化简、输出调度;
ItemDegs:自定义三元组键类,重写equals()与hashCode(),适配 HashMap 去重合并;
class

复杂度分析
image

高复杂度方法梳理:
1.optimizeTwo:DFS 递归实现三角函数化简,递归遍历层数不确定,循环复杂度偏高;
2.PolyList构造方法代码行数近百行,输入拆分、多类因子匹配、异常校验全部堆积在此,分支极多,方法调用耦合度iv(G)居高不下,违背单一职责;
3.三套重载print输出方法延续第一次作业问题,各类符号、系数、三角函数输出判断堆砌,分支冗余,圈复杂度持续偏高。

Bug 分析
公测情况
全部正确性用例通过;仅性能分未满分,原因仅做了基础三角函数合并,未设计贪心拆项、最简式压缩策略,化简深度不足,输出表达式长度偏长。

互测情况
自身共被测出 4 个 Bug,逐条复盘:
1.HashMap 删除异常:调用remove()移除键值对失效,键对象看似相同但删除失败;本质是自定义ItemDegs哈希与相等判定存在隐性漏洞,哈希值不一致导致 Map 无法匹配目标 key;
2.空白字符校验错误:误用Matcher.matches()全局匹配判断空白,应当使用find()匹配字符串内部任意位置非法空格;
3.巨型正则 + 贪婪模式引发 TLE:输入正则写法冗余、贪婪回溯次数爆炸,修改为非贪婪模式、拆分分段正则后超时问题解决;
本次个人未 Hack 到他人 Bug,核心原因对拍器存在缺陷:Sympy equals() 对复杂三角表达式等价判定不稳定,存在误判,第三次作业针对性重构校验方案。

自测测试方案
延续自动生成表达式思路,针对三角函数场景定向造数;表达式正确性不再单纯依赖 Sympy 等价判断,初步尝试代入多点数值验算降低误判概率。

第六次作业总结
作业要求
全面嵌套拓展:支持括号表达式因子、三角函数多层嵌套结构,完成嵌套表达式语法校验、递归求导、表达式化简、格式输出。

实现方式
1.词法解析:采用递归下降分析法拆分表达式,逐层拆解括号、运算符、各类嵌套因子;
2.数据结构:构建表达式二叉树,叶节点为常数、幂函数,中间节点为加减乘运算符、三角函数节点;
3.求导逻辑:对表达式树深度优先遍历(DFS)递归求导,生成求导后全新表达式树;
4.化简优化:遍历表达式树执行常量合并、乘 1、乘 0、冗余括号消除等基础化简规则。

代码规模
image

类图设计
Poly:顶层表达式入口类;
Item:表达式单项封装类;
CalculateTree:表达式树整体管理类,封装建树、求导、化简核心逻辑;
Node:二叉树节点基类,区分运算节点、函数节点、叶子常量节点;
class

复杂度分析
image

高复杂度方法原因分析:
1.CalculateTree构造方法:使用双栈算法构建表达式树,内部频繁调用分词、匹配、括号校验等多个子方法,方法调用耦合度iv(G)很高;
2.deriv求导函数:递归 DFS 遍历整棵表达式树,路径分支多,圈复杂度偏高;
3.simplify树化简函数:递归遍历嵌套节点,内部大量分支判断匹配 0、1、常量运算、多余括号场景,嵌套分支臃肿,结构化程度偏低。

三次作业复杂度共性优化思路总结
1.利用继承 + 多态拆分各类因子:为常数、幂函数、sin、cos、括号表达式分别创建子类,各自独立实现求导、化简、打印方法,避免所有逻辑挤压在同一个大类,从根源降低单个方法圈复杂度;
2.遵循单一职责拆分巨型方法:把超长构造、超长输出方法拆分为多个小型工具方法,一个方法只负责一类逻辑,减少多层 if 嵌套;
3.提前封装通用工具类:正则匹配、字符串裁剪、符号处理、哈希判相等公共逻辑抽离,减少代码重复与耦合。

Bug 分析
公测情况
功能正确性全部通过,无逻辑错误;性能分未满,短板在于化简策略不完善,多余括号消除只做浅层处理,面对多层嵌套冗余括号压缩能力弱,对比极致优化代码输出更长。

互测情况
自身全程未被测出功能性 Bug;人工排查找到三名同学三处典型问题:
1.指数边界理解错误:题目规定指数≤10000,部分代码误写成严格小于 10000;
2.常数项求导特殊处理缺失,常数求导结果为空字符串,未输出 0;
3.求导后表达式拼接不规范,符号、括号排版不符合输出格式要求。
感悟:自动化随机测试有局限性,针对性人工构造边界测试用例,更容易发现理解类、边界类隐蔽错误。

自测测试方案
全黑盒测试模式:
1.随机生成多层嵌套三角函数、嵌套括号表达式批量用例;
2.正确性校验:选取上百组不同自变量代入求值,对比 Sympy 运算结果判定等价;
3.格式判定:Python 捕获解析异常,判断输入是否属于非法格式。

关于设计模式的反思
本单元三次迭代结束后复盘,我发现自己对接口、继承理解偏表层,工厂模式未能在第三次作业落地使用。三种因子、表达式节点如果采用工厂模式 + 接口定义统一约束求导、化简、输出行为,代码分层会更清晰,后续拓展新函数类型成本极低。后续我会针对性补足设计模式场景化练习,在后续多线程、图形界面等单元,主动落地设计模式,深化面向对象工程化设计思想。

单元整体总结
一、学习收获
1.语法与算法层面
熟练掌握 Java 容器使用、字符串处理、正则表达式、递归下降、表达式二叉树、DFS 深度遍历等知识点;从最开始面向过程平铺式编码,逐步理解封装、继承、多态核心思想,学会基于迭代需求做架构设计。
2.工程迭代思维
直观体会迭代开发模式:一版实现基础功能、二版重构存储适配拓展、三版重构整体架构支撑嵌套逻辑,理解前期架构设计对后期拓展性的决定性作用,明白 “不能写完即结束,要预留迭代空间”。
3.测试与排错能力
搭建自动化对拍测试体系,掌握自动造数、第三方库校验、多点代入验算、静态代码审计多种测试手段;遇到 Bug 不再盲目改代码,能够定位根源、分析耦合与分支漏洞,形成规范排错思路。
4.代码度量与重构意识
学会看懂圈复杂度各项指标,能识别臃肿方法、高耦合类,主动思考拆分重构方案,不再只追求 “能跑就行”,兼顾可读性、可维护性。

二、现存短板与后续提升方向
1.设计模式运用生疏:仅能看懂基础定义,难以主动结合业务选用合适模式,后续针对性练习工厂、策略、组合模式等常用场景;
2.算法优化意识不足:前期只保证正确性,缺少复杂度预判,后续练习拓扑排序、递归剪枝、哈希优化等手段优化代码效率;
3.异常体系不规范:异常处理零散,没有统一异常分类与捕获机制,后续学习 Java 自定义异常体系,规范非法输入、运算异常管理;
4.正向设计习惯欠缺:常写完代码再补类图,今后养成先 UML 建模、梳理依赖结构,再动手编码的开发习惯,降低后期重构成本。

三、单元整体感悟
多项式三次迭代作业完整复刻了小型软件版本迭代全过程,也让我真正读懂面向对象设计的意义:不是语法噱头,而是用来应对需求迭代、降低维护成本的工程方案。下一单元我会吸收本单元踩过的耦合臃肿、考虑不周、测试不全等教训,提前做好架构规划,规范编码,主动运用设计模式,稳步提升面向对象程序设计综合能力。

posted @ 2026-06-24 20:15  SeongLu  阅读(2)  评论(0)    收藏  举报