2026_BUAA 面向对象程序设计第一单元总结

甄誉涵-24373397 2026-03-29 11:36:23

Unit1-设计总结

1. 程序结构度量分析

1.1 方法复杂度指标分析

img

  • 认知复杂度整体正常,但OutputManager中展开项的方法以及对三目运算符的解析方法尤其高,说明嵌套深、可读性差,亟需重构。
  • 基本复杂度整体合格,说明分支数设计合理,逻辑较为清晰;但仍存在少量方法复杂度高,在后续作业中应该着重注意控制流的书写。
  • 设计复杂度整体较低,但有关多项式的方法偏高,说明耦合度较高,可以借鉴他人代码架构学习优化。
  • 圈复杂度整体合格,说明分支设计与职责分发比较合理

1.2 类复杂度指标分析

img

  • 平均操作复杂度在代码主体业务逻辑相关类表现均良好,只有OutputManager类较高,需要继续学习类的职责划分。
  • 最大操作复杂度整体良好,只有OutputManager类较高。
  • WMC整体表现良好,但多项式类格外搞,需要继续探讨好的架构设计方法,并将部分应该分发到单项式的职责重构。

1.3 项目复杂度指标分析

Projectv(G)avgv(G)tot
unit12.48355
  • 平均复杂度良好,值得总结的经验是应该多阅读相关资料,如第一周的实验与练习代码、PPT、OO加油站等

1.4 代码架构与分析

img

本单元笔者的架构是一以贯之的迭代产物,将整体架构分为三个部分:主控制流、表达式树、计算载体。

本单元的处理逻辑总体上可以分为四个部分:预处理、表达式解析、计算、优化输出。

1.4.1 表达式树相关类

Expr:储存表达式(因子),存储指数与项序列,项序列中存储表达式用+/-分割后的项。

Term:储存项,存储项符号(直接用Token统一处理)与因子序列,因子序列中存储用*分割的因子。

Factor:因子接口,需实现展开方法(toPoly)。

因子接口的设计原因:由于因子的逻辑层次较低但抽象层次较高,所以更适合设计成接口而非继承。

Variable:变量(幂函数)因子,存储变量名(String)、指数,便于后续迭代中可能的其他名字的变量。

Constant:常数因子,用BigInteger防止溢出

ExpFunc:指数函数因子,存储指数和实参因子(在“解析”步骤不做计算处理,直接存储表达式子树,减少不必要的时间开销)

Ternary:选择式因子,存储四个位置的实参因子

Func:非递推函数因子,存储实参因子

RecurFunc:递推函数因子,存储调用序号和实参因子

Derivative:求导因子,存储求导类型(直接存储TokenType统一处理)和内部实参因子

存储实参因子的处理方法:直接储存表达式子树,减少不必要的分支时间开销。

1.4.2 表达式解析流程相关类

InputManager:工具类预处理输入字符串,得到格式统一、结构简单的便于统一处理的输入字符串。

InputData:存储预处理后的数据,包括函数个数,待解析字符串

Parser:文法解析器,解析来自LexerToken流,将表达式(或函数式)转化为

Lexer:词法解析器,将字符按照词法要求转化为Token流交予Parser处理

TokenTokenType:词元及词元类型

1.4.3 函数解析相关类

RecurDef:递推定义类,用于存储递推定义以便函数解析

Definer:函数定义工具类,用于存储所有函数的定义,便于解析使用,采用单例模式,避免频繁传参导致循环引用

1.5 处理流程分析

  1. 预处理:接收控制台输入,根据输入的函数个数标识符存储相应数量的字符串。

    • 对所有字符串执行去空白符计算连续符号的预处理手段
    • 对函数定义字符串额外执行去除前缀,确定顺序处理
  2. 函数解析:接收预处理后的数据的函数定义字段,将处理好的函数定义形参表达式存入函数定义工具类(单例)中

    • 针对非递推函数,解析函数定义的时候将函数名连同解析好的形参表达式存入普通函数定义器中
    • 针对递推函数,先解析初始定义,存入递推函数定义器中,再解析递推定义,利用递推定义直接计算出需要的所有递推形参表达式
  3. 表达式解析:字符串->Token流->Expr表达式树->Poly多项式

  4. 优化输出

1.6 架构优缺点分析

1.6.1 优点

  1. 流程设计:明确、清晰、可读性强,笔者将三次作业均采用相近的流程框架,便于理解并保持较高的可扩展性
  2. 程序解耦:主要流程相关类与方法解耦程度高,分工明确,职责清晰,可维护性强
  3. 输出独立:优化输出部分单独成块,避免与Poly类耦合,同时避免改写toString()重要方法
  4. 输入独立:将初步解析输入单独设置成类,避免主类流程控制复杂
  5. 单例模式的使用:设置Definer函数定义工具类单例,避免函数解析与调用过程中频繁传参存参导致循环引用等隐蔽问题
  6. 递推函数的解析策略:最优方案是,解析表达式时,遇到f{i}(factor)时,若未解析f{i}(x)形参表达式,则先递推解析f{i}(x)形参表达式,之后取出形参表达式,将x替换为实参factor,解析成Poly后存储factor -> Poly的映射关系,下次遇到相同factor时直接取出解析后的值;在均衡性能和实现难度之后,采用的方式是,先解析出所有f{i}(x)形参表达式并缓存,调用时直接取出处理。
  7. 迭代开发的扩展性预留:在Variable中有储存变量名,便于变量数量的扩展;在函数定义中保留函数名字段,便于函数种类的扩展。

1.6.2 缺点

  1. OutputManager复杂度较高:主要原因在于方法较冗长,可以做进一步拆分优化。
  2. PolyUnit在职责划分中不够清晰:可以将Poly中计算的子方法(涉及单项式的)放在Unit中实现
  3. 递推函数以及普通函数记忆化搜索的优化实现:可以再提升一些性能
  4. 性能优化与复杂度的Trade off:出于对复杂度与代码可读性的考量,笔者没有做

2. 架构设计体验

2.1 第一次作业

作业内容回顾:实现一个表达式解析器,解析单变量带幂次多层括号的表达式

架构设计:ParserLexerPolyExprFactorVarConstantProcessor

初步设计时,我采用的是所有表达式树相关节点都是基于Poly结构存储,在解析构建表达式树时同步计算,解析后直接输出为Poly,最终优化输出。

Unit单项式的定义(第一次作业):$a*x^i$

这种设计的优点是:思路直接,复杂度较低

这种设计的缺点是:未构建完整的表达式树;在解析分支时可能造成不必要的开销(着重体现在第二次作业的exp迭代中);没有做到“高内聚低耦合的设计思想”,职责划分不明确。

2.2 第一次作业后的重构

img

为提前准备第二次作业可能造成的架构问题,我在第二次作业开放前进行了项目的重构,并在第一次研讨课上分享了我对两种架构的理解,核心观点如下:

  1. 两种处理方式的比较:
    • 第一种:在构建表达式树的时候同步进行计算展开,数据结构完全基于PolyUnit类实现
    • 第二种:先构建表达式树(ParserLexer实现),再利用toPoly方法实现计算与展开
  2. 两种处理方式的选择:
    • 功能角度分析:分开处理的目的是构建完整的表达式树,在不计算、开销小的情况下完整保留分支
    • 需求角度分析:若需求中需要表达式树特有的信息(如需要用到表达式树深度参与运算),则解耦后更适合;本次作业的业务逻辑主要基于多项式和字符串展开,因此边解析边计算的方式也适用;在第二次作业中,选择表达式的加入则需要考虑更多因子、更复杂的分支开销,则第二种方式更合适。

2.3 第二次作业

第二次作业回顾:新增指数函数因子、选择表达式因子、函数调用因子

与第一次作业相比,笔者主要分步骤、分层次做了以下迭代工作:

  1. exp因子增加:横向对比1、2次作业,笔者发现指数函数因子的添加是最基础与最核心的,因此,笔者选择优先单独迭代、单独测试指数函数因子。这一部分需要修改单项式与多项式的基础定义与计算方法。Unit的定义(第二次作业):$ax^iexp(Poly)$
  2. 实现函数定义核心工具单例Definer:在本次作业,只需存储非递推函数的标识符到形参表达式的映射即可。
  3. 选择表达式的解析:在重构后的架构基础上,笔者选择新增Ternary类,存储解析后的四个因子的表达式子树,在计算解析(toPoly)时再判等解析,非必要分支过于臃肿的特定样例下若先解析再存储会导致TLE,然而重构后的架构“迫使”笔者新增因子类,构建表达式树之后再解析,间接规避掉这类TLE问题。
  4. 在多层嵌套情境下的深克隆开销风险:在debug章节笔者将详细分析。

2.4 第三次作业

第三次作业回顾:支持双变量,新增求导因子,新增自定义递推函数

与第二次作业相比,笔者主要分步骤、分层次做了以下迭代工作:

  1. 双变量的迭代:横向对比2、3次作业,笔者发现双变量的添加是最基础与最核心的,因此,笔者选择优先单独迭代、单独测试指双变量相关内容。只需要扩充单项式的定义为$ax^iy^j*exp(Poly)$,并实现相关计算方法,值得注意的是,在括号添加判定中有一个隐蔽的bug,在debug章节,笔者将详细介绍
  2. 求导因子的迭代:只需要增加新的因子类并在Poly类分别实现dxdygrad方法即可。
  3. 新增自定义递推函数:需要从主控逻辑到函数解析逻辑、因子解析逻辑的大范围改动,需要细心。

2.5 新的迭代场景

  1. 运算层面:可以新增有关表达式深度的运算符,如factorA@factorA,定义为$h_AfactorA+h_BfactorB$,其中$h$为因子所在的表达式树的深度
  2. 因子层面:可以新增三角函数因子sin(factor)cos(factor)整体处理逻辑和指数函数因子相差不大,主要难点在优化层面,涉及到众多三角函数公式的使用
  3. 函数层面:在限制cost的前提下,放宽对递推函数深度的限制,考虑极端情况,若深度不限,则必须强制实现缓存甚至记忆化的处理。

3. debug分析

在第一单元的迭代过程中,笔者的代码有幸通过所有强测用例,并经受住所有hack,但在迭代的过程中我也遇到了和在互测中其他同学存在的bug,在下文一并分析

# 样例一
1
f(x)=x+2*x^2+3*x^3+4*x^4+8*x^8*exp(x)
[(x==1)?f(f(f(f(f(f(f(f(f(x))))))))):f((x+1))]

# 样例二
1
f(x)=exp(exp(exp(exp(exp(exp(x)^2)^2)^2)^2)^2)^2
0
f(f(f(f(x))))

# 样例三
0
0
exp((x*y))
  1. 选择式分支短路处理:涉及样例为样例一,如果选择先解析两个分支而不是先储存表达式树不解析,则会出现单个分支本不需要解析而解析导致的时间开销大的问题
  2. 多层嵌套下深克隆等原因导致的时间开销:涉及样例为样例二,如果对是否需要深克隆理解不清导致全部深克隆会导致时间复杂度呈指数增长,其他诸如字符串替换,幂次运算不合理等问题也会导致TLE
  3. 迭代中的遗留问题:涉及样例为样例三是不是朴实无华呀),这个样例对应的问题是在第二次到第三次作业的迭代过程中,对不需要加括号的项的判定只有x因此加上y之后未修改,导致错误,这让笔者反思合理架构的重要性,因为这个问题本质是缺少正确的解耦。
  4. 过度优化导致的时间开销:如果对算法本身了解不足而盲目或使用大模型进行优化,则会导致复杂度超标,最明显的过度优化是不合理的状态压缩动态规划。

4. 优化分析

4.1 输出方面

在本单元的作业中,我主要做了以下集中基础但不易出错的优化

  • 如果单项式系数为0,则最终结果为0,
  • 如果单项式系数为1,则可以省略系数,简化为$x^n$
  • 如果单项式系数为-1,则可以省略系数,简化为$-x^n$
  • 如果单项式x指数为0,则只输出系数
  • 如果单项式指数为1,则简化为$ax$​
  • 将所有正项放在负项之前
  • 尝试寻找exp((Poly))中合适的公因数提出

4.2 性能方面

  • 多项式幂运算时使用快速幂算法
  • 利用HashMap自动实现合并同类项的操作
  • 递推函数的缓存处理

5. 大模型的使用

笔者在本次作业正确性相关的部分中大概有10%左右的工作交予大模型完成,主要用于修改checkstyle时拆分方法与分支,涉及到的类是ParserLexer;在极少部分方法中笔者利用AI查询API与调整代码结构,使得单个方法更优雅美观;在优化过程中,笔者撰写方法时会将需求告诉大模型,让其辅助给出步骤或伪代码、以及可能用到的API,具体实现由人工完成;在完成并整体审查代码正确性后,笔者会让AI优化单个方法的实现逻辑,并利用AI检查代码可能出现的隐蔽错误与健壮性考量;同时,笔者在评测机或数据生成过程中利用AI辅助。

暂时无法确定互测房间内同学大量使用AI生成代码

在理论课中,老师建议我们自己设计代码框架,这也是面向对象的核心所在,因此,在三次作业的代码框架设计中,笔者完全自主实现,比较推荐的参考资料是OO加油站、实验课与练习代码。

6. 心得体会

​ 本单元我们通过表达式解析迭代作业的训练,初步掌握了,面向对象的设计思想,理解了继承与接口的选择方法,对递归下降的输入解析方法有了更深刻的认知。在本单元的学习过程中,我参与了两次研讨课的分享活动,对代码架构的设计选择有了更深刻的理解,其中让我印象最深的是,老师在讲到类的设置的时候,提到有三种职责的类:关注输入输出、关注对象管理、关注流程控制,这让我对面向对象的思想有了更具体的体会。老师还提到了有关设计模式的内容,让我对下一单元的学习充满期待~加油!!!

7. 未来方向

可以增加更多高质量的资料分享,如OO加油站的高质量文章,更多的练习代码等

...全文
117 回复 打赏 收藏 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

309

社区成员

发帖
与我相关
我的任务
社区描述
2026年北航面向对象设计与构造
java 高校
社区管理员
  • 孙琦航
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧