2026 BUAA-OO-Unit1总结

杨启桐24373180 2026-03-27 21:40:15

一、项目结构度量分析

代码度量分析

ClassOCavgOCmaxWMC
ChoiceFactor1.5023
DeriveFactor1.5023
ExpFactor1.5023
Expr1.5023
ExprFactor1.0012
Factor1.0013
FuncCallFactor1.0012
FuncDef1.0013
Lexer4.002240
MainClass3.0059
Mono1.82520
NumFactor1.0012
Parser5.181557
Poly4.251085
RecursiveFuncCallFactor1.0012
RecursiveFuncDef4.0078
RecursiveFuncDefParser3.33820
Term1.3324
Token1.0013
Token.Typen/an/a0
VarFactor1.5023
MethodCogCev(G)iv(G)v(G)
ChoiceFactor.ChoiceFactor(Factor, Factor, Factor, Factor)0111
ChoiceFactor.toPoly()2222
DeriveFactor.DeriveFactor(Expr, String)0111
DeriveFactor.toPoly()2122
ExpFactor.ExpFactor(Factor)0111
ExpFactor.toPoly()1222
Expr.addTerm(Term)0111
Expr.toPoly()1122
ExprFactor.ExprFactor(Expr)0111
ExprFactor.toPoly()0111
Factor.getIndex()0111
Factor.setIndex(int)0111
Factor.toPoly()0111
FuncCallFactor.FuncCallFactor(FuncDef, Factor)0111
FuncCallFactor.toPoly()0111
FuncDef.FuncDef(String, Expr)0111
FuncDef.getBody()0111
FuncDef.getParamName()0111
Lexer.Lexer(String)0111
Lexer.getCurrentToken()0111
Lexer.getIndex()0111
Lexer.handleSign(char)151815
Lexer.isEnd()0111
Lexer.nextToken()0111
Lexer.parseNumber(int)3155
Lexer.preProcess(String)1122
Lexer.setIndex(int)0111
Lexer.tokenize()3013537
MainClass.findSingleEquals(String)6367
MainClass.main(String[])4155
MainClass.parseFuncDef(String)0111
Mono.Mono(BigInteger, BigInteger)0111
Mono.Mono(BigInteger, BigInteger, BigInteger, Poly)3123
Mono.Mono(BigInteger, BigInteger, Poly)3123
Mono.getCoeff()0111
Mono.getExpInner()0111
Mono.getXExp()0111
Mono.getYExp()0111
Mono.isSameType(Mono)6517
Mono.multiply(Mono)5145
Mono.negate()0111
Mono.withCoeff(BigInteger)0111
NumFactor.NumFactor(String)0111
NumFactor.toPoly()0111
Parser.Parser(Lexer)0111
Parser.isConditionWrapped()142712
Parser.parseChoiceFactor()1611515
Parser.parseDeriveFactor(String)4155
Parser.parseExpFactor()4155
Parser.parseExpr()9167
Parser.parseFactor()2211820
Parser.parseFuncCall()1821515
Parser.parseTerm(int)2133
Parser.setFuncDef(FuncDef)0111
Parser.setRecFuncDef(RecursiveFuncDef)0111
Poly.Poly()0111
Poly.Poly(int)0111
Poly.addMono(Mono)0111
Poly.addPoly(Poly)0111
Poly.derive(String)1122
Poly.deriveMono(Mono, String)225810
Poly.equals(Object)2210714
Poly.expToString(Poly)126511
Poly.formatSingleGroup(ArrayList, BigInteger)181614
Poly.hashCode()0111
Poly.isZero()0111
Poly.monoToString(Mono)131911
Poly.multiPoly(Poly)3133
Poly.negate()1122
Poly.polyToString(Poly)125610
Poly.powerPoly(BigInteger)6255
Poly.print()1222
Poly.simplify()10556
Poly.substitute(Poly)6144
Poly.subtractPoly(Poly)0111
RecursiveFuncCallFactor.RecursiveFuncCallFactor(RecursiveFuncDef, int, Factor)0111
RecursiveFuncCallFactor.toPoly()0111
RecursiveFuncDef.RecursiveFuncDef(Expr, Expr, BigInteger, Factor, int, BigInteger, Factor, ...)0111
RecursiveFuncDef.evaluate(int)15277
RecursiveFuncDefParser.expect(Lexer, Type)2133
RecursiveFuncDefParser.findSingleEquals(String)6367
RecursiveFuncDefParser.parse(String, String, String)2122
RecursiveFuncDefParser.parseCoeff(Lexer)2133
RecursiveFuncDefParser.parseRecurrence(String, Expr, Expr)131811
RecursiveFuncDefParser.skipUntil(Lexer, Type)3144
Term.Term(int)0111
Term.addFactor(Factor)0111
Term.toPoly()1122
Token.Token(Type, String)0111
Token.getType()0111
Token.getValue()0111
VarFactor.VarFactor(String)0111
VarFactor.toPoly()2122

我们可以发现整体项目代码的核心在于parser,lexerpoly这三个类,他们的主要作用分别是语法分析、词法分析和多项式处理。

经典度量分析

内聚度分析

总体来看,本次设计的代码架构属于高内聚情况。代码的高内聚比例达到了65%,其中主要的类都属于高内聚(Poly, Lexer, Parser, Mono, Token 等核心类);而中等内聚主要是一些拥有大量数据存储型属性的类,他的占比20% (RecursiveFuncDef, Expr, Term)。最后的低内聚占比约为15%,其中均为Factor的子类。

耦合度分析

入度*出度**扇出耦合强度
Poly92强(中心)
Parser28强(枢纽)
Lexer22
Factor110中(父类)
Mono51
Token20
Expr31
Term21

本次设计的代码的总体耦合度评估为中等。
我们代码中的主要耦合链如下:

MainClass 
  → Lexer(词法分析)
  → Parser(语法分析)
  → Expr → Term → Factor(表达式树)
  → Poly → Mono(计算核心)

而在Parser和各个Factor子类以及递归类之间的耦合则较弱。
总体来说,本次代码设计的主要耦合集中在了框架定义方面————词法-语法-语义。

二、项目架构

总体架构展示

┌─────────────────────────────────────────────┐
│         应用层:MainClass                    │
│      (输入 → 解析 → 计算 → 输出)           │
└────────────┬────────────────────────────────┘
             │
┌────────────▼────────────────────────────────┐
│   词法分析层(Lexer)& 语法分析层(Parser)  │
│   职责:String → Token序列 → 抽象语法树      │
└────────────┬────────────────────────────────┘
             │
┌────────────▼────────────────────────────────┐
│  语义处理层:Poly & Mono(数学计算核心)    │
│  职责:抽象语法树 → 多项式 → 结果输出        │
└─────────────────────────────────────────────┘

完整架构展示

img

架构处理过程展示


┌──────────────────────────────────────────────────────────────────────┐
│                          应用层:MainClass                            │
│                    负责整个流程的协调和结果输出                        │
└────────────────────────────┬─────────────────────────────────────────┘
                             │
┌────────────────────────────▼──────────────────────────────────────────┐
│                     第一层:词法分析(Lexer)                          │
│  ┌──────────────────────────────────────────────────────────────────┐ │
│  │ preProcess()  → tokenize()  → handleSign() → parseNumber()       │ │
│  │ (预处理)      (主要流程)     (符号处理)    (数字解析)             │ │
│  └──────────────────────────┬───────────────────────────────────────┘ │
│                             │                                          │
│                        String → Token[]                                │
└────────────────────────────┬──────────────────────────────────────────┘
                             │
┌────────────────────────────▼──────────────────────────────────────────┐
│                     第二层:语法分析(Parser)                         │
│  ┌──────────────────────────────────────────────────────────────────┐ │
│  │  parseExpr()                                                      │ │
│  │    ↓ (处理 + 和 -)                                               │ │
│  │  parseTerm()                                                      │ │
│  │    ↓ (处理 * 和 /)                                               │ │
│  │  parseFactor()                                                    │ │
│  │    ↓ (处理幂运算、括号、函数调用、三目表达式)                    │ │
│  │  Factor 多态调度(13个子类)                                     │ │
│  │    - NumFactor / VarFactor / ExpFactor                           │ │
│  │    - FuncCallFactor / DeriveFactor / ChoiceFactor               │ │
│  │    - RecursiveFuncCallFactor / ...                              │ │
│  └──────────────────────────┬───────────────────────────────────────┘ │
│                             │                                          │
│                    Token[] → Expr → Poly                              │
└────────────────────────────┬──────────────────────────────────────────┘
                             │
┌────────────────────────────▼──────────────────────────────────────────┐
│              第三层:表达式结构(Expr → Term → Factor)               │
│  ┌──────────────────────────────────────────────────────────────────┐ │
│  │  Expr                    Term                    Factor           │ │
│  │  ├─ List<Term>          ├─ List<Factor>        ├─ abstract       │ │
│  │  ├─ addTerm()           ├─ addFactor()         ├─ toPoly()       │ │
│  │  └─ toPoly()            └─ toPoly()            └─ setIndex()     │ │
│  └──────────────────────────────────────────────────┬────────────────┘ │
│                                                      │                  │
│  Factor.toPoly() 递归调用 Term.toPoly(),                             │
│  Term.toPoly() 递归调用 Factor.toPoly()                              │
└────────────────────────────┬──────────────────────────────────────────┘
                             │
┌────────────────────────────▼──────────────────────────────────────────┐
│              第四层:函数管理(Function & Recursion)                  │
│  ┌──────────────────────────────────────────────────────────────────┐ │
│  │  FuncDef                    RecursiveFuncDef                      │ │
│  │  ├─ name: String            ├─ base0, base1: Expr               │ │
│  │  ├─ body: Expr              ├─ cache: HashMap<Int, Poly>        │ │
│  │  ├─ call(Poly)              ├─ evaluate(int n)                  │ │
│  │  └─ substitute()            └─ 记忆化优化(O(2^n)O(n))      │ │
│  │                                                                    │ │
│  │  FuncCallFactor ──→ FuncDef.call()                               │ │
│  │  RecursiveFuncCallFactor ──→ RecursiveFuncDef.evaluate()        │ │
│  └──────────────────────────────────────────────────────────────────┘ │
└────────────────────────────┬──────────────────────────────────────────┘
                             │
┌────────────────────────────▼──────────────────────────────────────────┐
│          第五层:数学计算核心(Mono & Poly)【关键层】               │
│  ┌──────────────────────────────────────────────────────────────────┐ │
│  │  Mono(单项式)                    Poly(多项式)                │ │
│  │  ┌────────────────────────────┐   ┌─────────────────────────┐   │ │
│  │  │ • coeff: BigInteger        │   │ • monos: List<Mono>    │   │ │
│  │  │ • xexp: BigInteger         │   │ • addPoly()            │   │ │
│  │  │ • yexp: BigInteger         │   │ • multiPoly()          │   │ │
│  │  │ • expInner: Poly ★★★      │   │ • simplify()           │   │ │
│  │  │                            │   │ • derive()             │   │ │
│  │  │ 方法:                      │   │ • powerPoly()          │   │ │
│  │  │ • multiply()               │   │ • substitute()         │   │ │
│  │  │ • isSameType()             │   │ • toPoly() / print()   │   │ │
│  │  │ • withCoeff()              │   │                        │   │ │
│  │  └────────────────────────────┘   └──────┬──────────────────┘   │ │
│  │                                           │                      │ │
│  │                  ◆ 递归关系(关键创新):                        │ │
│  │          Mono 中的 expInner 指向 Poly                           │ │
│  │          Poly 的 monos 包含 Mono                                │ │
│  │          这形成了自然的递归结构,支持 exp 无限嵌套 ◆           │ │
│  └──────────────────────────────────────────────────────────────────┘ │
│                                                                        │
│  运算示例:                                                            │
│  • exp(x) → Mono(1, 1, Poly{x})                                      │
│  • exp(exp(x)) → Mono(1, 0, Poly{Mono(1, 1, Poly{x})})              │
│  • (x+1)^2 → Poly{Mono(1,2), Mono(2,1), Mono(1,0)} → simplify      │
└────────────────────────────┬──────────────────────────────────────────┘
                             │
┌────────────────────────────▼──────────────────────────────────────────┐
│                   输出:格式化字符串(toString())                     │
│            • 动态规划优化:公因式提取                                 │
│            • 缓存机制:避免重复计算                                   │
└──────────────────────────────────────────────────────────────────────┘

优点:架构清楚明了,支持多次的迭代和优化,将整个代码分成了三大板块,各自分工明确。
不足Pasrser的语法分析过于冗长,Factor子类过多

架构新迭代情形展望

三、项目架构设计过程

一、第一次作业:基础架构(仅支持加减乘幂,变量仅x)

第一次作业我采用了三层架构来处理多项式表达式。将字符串通过逐层解析转化为多项式对象,再进行数学运算。

数据结构: 简单的系数-幂次映射

HashMap<Integer, Integer>
key: x 的幂次
value: 该项的系数

解析器: 递归下降解析

Expr → Term → Factor
按优先级处理加减、乘法、幂运算

计算层: Poly 类统一处理所有运算

  • addPoly():多项式加法
  • multiPoly():多项式乘法
  • powerPoly():多项式幂运算

评价:架构清晰简洁,但 无法表示 exp(x) 这样的项,限制了功能扩展。

二、第二次作业:底层重构(支持 exp、函数、三目表达式)

第二次作业需要支持 exp 嵌套、函数定义等复杂需求,原有的 HashMap 数据结构无法满足。我引入了 Mono 类 来表示更复杂的项,改用 ArrayList<Mono> 存储,并为 exp 运算添加了新的方法。

核心改进:Mono 类的递归设计

class Mono {
    BigInteger coeff;        // 系数
    BigInteger xexp;         // x 的幂次
    Poly expInner;          // exp 内部的多项式(递归!)
}

在这里我使用了递归结构:在Mono 中包含 Poly,而 Poly 是 List<Mono>,形成了自然的递归关系。这样我们就可以表示任意深度的 exp 嵌套了。
同时我也需要重新设计几个运算逻辑,例如:

  • isSameType():判断同类项时需要比较 exp 内容,不仅仅是幂次.
  • multiply():两项相乘时,exp 相乘遵循规则 exp(u)·exp(v) = exp(u+v).
  • simplify():合并同类项需要考虑 exp 信息.

最后我新增了内容以便支持这次迭代:用于支持函数定义和调用的Function 类;用于处理函数调用因子的FuncCallFactor;以及处理三目表达式 的ChoiceFactor;最后是处理 exp() 函数的ExpFactor

评价:保留了第一次的架构框架,通过新增 Mono 类和改变存储方式优雅地扩展了功能。但 ArrayList 的顺序存储导致合并同类项时需要 O(n²) 的复杂度,性能有所下降。不过这个设计足够稳定,为第三次作业的扩展奠定了坚实基础。

三、第三次作业:功能完善(支持递推函数、导数,新增变量 y)

第三次作业不需要在底层进行重构,他只需要在第二次稳定的数据结构基础上,进一步扩展运算功能。我新增了 RecursiveFuncDef 类独立处理递推函数,在 Poly 类中添加 derive() 方法处理导数运算,同时支持了 y 变量。

核心设计

  • 递推处理RecursiveFuncDef 类用 HashMap 缓存计算结果,实现记忆化优化
  • 导数运算:在 Poly 层面分解链式法则,最终拆分为基本的加减乘运算
  • 变量扩展:Mono 中新增 yexp 字段存储 y 的幂次

评价:在不改动底层数据结构的前提下,通过设计新类和扩展已有方法优雅地实现了新功能,体现了第二次重构设计的成功。

总结

三次作业的迭代过程比较流畅,主要问题在于用ArrayList去储存,这导致了代码性能方面的下降。

四、Bug 分析

自己代码中的 Bug

问题 1:符号解析逻辑过于集中

现象:第二次作业中,Lexer 的 handleSingle 方法对于前导符号和运算符号的解析存在混乱,导致某些符号组合无法正确处理。

根本原因handleSingle 方法职责过重,试图在一个方法中处理多种不同的符号场景。

解决方案:引入 preProcess() 前置处理方法,负责预先对相邻符号进行合并和化简,然后再将规范化后的 token 流传入 handleSingle。这样通过分工明确的两个方法替代了一个臃肿的方法,提高了代码的可维护性。

问题 2:大指数运算溢出且性能差

现象:计算多层嵌套的指数时,指数的数值超出了int存储范围,同时计算速度太慢。

解决方案:我把将指数改用 BigInteger 存储,支持任意大小的指数,同时相应的修改了相关方法;同时在指数计算时采用了快速幂算法,将时间复杂度降至 O(log n)

他人代码中发现的问题

虽然没有搭建完整的测评机,但通过分析同学代码,我发现了一些规律性的问题:

第一次作业:主要问题集中在符号组合处理上,特别是在连续符号的识别。我针对这类场景构建了测试数据。

第二、三次作业:主要问题出现在函数运算大规模展开后的项合并上。在函数递推或乘法展开后,如何正确识别和合并同类项成为关键。

总体而言,我在互测过程之中发现的实质性 bug 并不多,这反映出大多数同学代码的基础实现是正确的。不过这也让我意识到搭建完整的自动化测评机确实是必要的,能够系统地发现边界情况和性能问题。

五、优化

自己代码中的优化

1. 三目表达式的解析

对于 [condition ? exprA : exprB],
根据 condition 的结果只解析需要的分支,
跳过不需要的另一分支的解析
→ 减少不必要的计算

2. 输出优化(toString 方法)
通过动态规划和公因式提取,优化输出字符串的长度。例如,3x + 6x + 9x 可以输出为 3(x + 2x + 3x) 或直接合并为 18x

3. 递推函数的记忆化缓存
使用 HashMap 缓存已计算过的递推值,避免重复计算。时间复杂度从 O(2^n) 降至 O(n)。

六、大模型使用

对于大模型的使用率估计(独立计算)

序号正确性性能
第一次作业5%10%
第二次作业10%30%
第三次作业10%0%

大模型的主要使用场景:第一次作业中的输出序列优化;第二次作业中的动态dp算法以及三目运算中项的分解;第三次作业递归函数的计算。

其余应用场景

  • 在同房互测环节,我使用了AI来为我讲解同学代码的大致思路,并且详细了解优化和计算的逻辑。
  • 在本次博客作业之中,我让AI为我辅助生成了一些表格和流程展示。

使用效果

对于代码的优化部分,大模型完成的很好,我的动态dp算法由大模型生成,同时,我的递归函数计算过程也由大模型指导,在这两个小模块代码之中,大模型的完成效果显著。
对于寻找bug环节,大模型实现的效果并不理想,很多时候会虚构出一些bug或者构造不符合输入原则的序列。

七、心得体会

在本次作业中,我自己最大的体会就是底层代码的重要性和性能对于代码影响的重要性。由于在第二次作业的时候为了方便而使用了ArrayList去存储Mono,我的代码在性能方面一直处于劣势,而这一错误也一直在我的代码中贯穿,直到总结时才发现自己的问题。
同时,明确自己代码的架构对于程序的构建也至关重要。在多次的迭代之中,我们需要抓住本质的架构,在修改方法、增加变量、增改类的繁琐工作之中,只有掌握好自己代码的框架,在每次迭代之前明确自己的修改范围和方法,才能够保证自己的改进能够完整而正确。

八、未来方向建议

可以在第一次作业时在指导书之中加入对于代码架构设计的指引,这样可以让同学们不用在之后的迭代中反复推倒重建自己的代码架构。

...全文
31 回复 打赏 收藏 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
代码下载链接: https://pan.quark.cn/s/a4b39357ea24 用户账户控制(UAC)白名单的配置 Windows7环境中 UAC(User Account Control,用户帐户控制)是由微软在Windows Vista版本中推出的一项旨在增强系统安全性的创新技术,该技术强制要求用户在执行可能干扰计算机正常运作的操作或进行更改会波及其他用户设置的变动前,必须提供相应的权限或管理员密码进行验证。通过对这些操作启动前进行授权确认,UAC能够有效阻止恶意软件及间谍软件在未获授权的状态下于计算机内进行安装或实施修改。 自从Vista版本问世以来,微软便开始推行这一全新的安全机制,可视为对系统安全防护的显著提升。尽管UAC确实能够在一定程度上对某些非法程序起到防御作用,但与此同时,这一功能也给众多用户带来了诸多不便。 因此,许多用户开始探寻是否存在类似于白名单的功能,以便将那些值得信赖的程序直接赋予运行权限。事实上,这类功能确实存在,不过微软并未将其作为标准配置提供。 网络上关于此问题的绝大多数建议都是建议禁用UAC,这种说法显然缺乏针对性,因为若用户希望禁用此功能,本就不会提出相关疑问。 通过运用微软官方发布的Microsoft Application Compatibility Toolkit 5.6版本,可以将信任的程序纳入系统白名单范畴。 获取Application Compatibility Toolkit 安装程序成功后会出现三个可执行文件 以管理员身份启动Compatibility Administrator 在Custom DataBases部分创建新的数据库,并添加一个Application Fix(在下方空白处点击右键,选择...

309

社区成员

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

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