面向对象第一单元总结博客

吕子颉-74066206 2026-03-31 16:51:50

面向对象第一单元总结博客

1. 基于度量来分析自己的程序结构

以下是第三次作业(HW3)代码的完整度量结果。

1.1 类级别度量结果 (Class Metrics)

类名 (Class)LOC (代码行数)CBO (类间耦合度)LCOM (方法内聚缺乏度)CSA (属性数量)CSO (方法数量)

Parser

177

15

1

4

15

Polynomial

215

6

1

2

33

Term

155

5

1

4

15

Tokenizer

82

4

1

3

5

Main

55

6

1

2

2

TokenType(枚举类) 

28

0

0

24

0

FuncCallNode

25

4

1

4

3

SelectNode

20

3

1

4

2

Token

18

1

1

2

3

DeriveNode

16

3

1

2

2

AddNode

12

2

1

2

2

ExpNode

12

2

1

1

2

MulNode

12

2

1

2

2

PowNode

11

2

1

2

2

ConstNode

9

1

1

1

2

ExprNode (接口)

5

1

0

0

1

1.2 分析(类层面)

  • 避免了类过度臃肿:项目中类的平均行数约为 55 行。

  • 最高耦合度的类:Parser (CBO = 15)

    • 分析:解析器类 Parser 是整个项目中耦合度最高的。它不仅依赖了 Tokenizer,还包含了所有的AST节点。若要进一步优化,可以引入工厂模式 (Factory Pattern),让 Parser 仅负责语法推导逻辑。

  • 最庞大的类:Polynomial (LOC = 215, CSO = 33)

    • 分析:多项式类拥有 33 个方法。它承担了多项式的加、减、乘、乘方、求导等重任。因此必须拆分足够多的辅助方法。

1.3 方法级别度量结果 (Method Metrics)

方法名 (Method)LOC (代码行数)v(G) (圈复杂度)

AddNode.AddNode(ExprNode, ExprNode)

4

1

AddNode.evaluate(Map<String, Polynomial>)

6

1

ConstNode.ConstNode(BigInteger)

3

1

ConstNode.evaluate(Map<String, Polynomial>)

4

1

DeriveNode.DeriveNode(ExprNode, String)

4

1

DeriveNode.evaluate(Map<String, Polynomial>)

10

3

ExpNode.ExpNode(ExprNode)

3

1

ExpNode.evaluate(Map<String, Polynomial>)

7

1

FuncCallNode.FuncCallNode(String, ExprNode, ...)

8

1

FuncCallNode.computeResult(Polynomial)

8

1

FuncCallNode.evaluate(Map<String, Polynomial>)

11

2

FuncCallNode.resolveBodyStr()

13

4

Main.main(String[])

26

4

Main.readRecurrenceGroup(Scanner, Map)

16

5

MulNode.MulNode(ExprNode, ExprNode)

4

1

MulNode.evaluate(Map<String, Polynomial>)

6

1

Parser.Parser(String, Map, Map)

7

1

Parser.consume()

3

1

Parser.parseConstFactor()

4

1

Parser.parseDeriveFactor()

16

3

Parser.parseExpFactor()

10

2

Parser.parseExprFactor()

8

2

Parser.parseExpression()

16

4

Parser.parseFactor()

15

5

Parser.parseFuncCall()

7

1

Parser.parseOptionalExponent()

10

3

Parser.parsePrimary()

22

5

Parser.parseSelectFactor()

14

1

Parser.parseTerm()

9

2

Parser.parseVarFactor()

6

2

Parser.peek()

1

1

Polynomial.Polynomial()

1

1

Polynomial.add(Polynomial)

6

3

Polynomial.addTerm(Term)

18

6

Polynomial.compareTo(Polynomial)

11

4

Polynomial.constant(BigInteger)

7

2

Polynomial.equals(Object)

11

7

Polynomial.isSingleFactor()

13

6

Polynomial.isZero()

3

1

Polynomial.multiply(Polynomial)

9

3

Polynomial.pow(int)

15

6

Polynomial.subtract(Polynomial)

13

3

Polynomial.toExpressionString()

24

6

Polynomial.toFactorString()

6

2

Polynomial.variable(int)

5

1

PowNode.PowNode(ExprNode, BigInteger)

4

1

PowNode.evaluate(Map<String, Polynomial>)

5

1

SelectNode.SelectNode(ExprNode, ...)

7

1

SelectNode.evaluate(Map<String, Polynomial>)

10

2

Term.Term(BigInteger, BigInteger, BigInteger, Polynomial)

7

1

Term.appendBody(StringBuilder, boolean, boolean, boolean)

35

10

Term.asPoly()

3

1

Term.compareTo(Term)

16

4

Term.deriveVariable(BigInteger, BigInteger, boolean)

23

7

Term.deriveX()

4

1

Term.deriveY()

4

1

Term.equals(Object)

14

6

Term.getCoefficient()

3

1

Term.getExpPoly()

3

1

Term.getXdeg()

3

1

Term.getYdeg()

3

1

Term.hashCode()

8

1

Term.merge(Term)

7

1

Term.multiply(Term)

8

1

Term.toFormatString(boolean)

18

6

Token.Token(TokenType, String)

4

1

Token.getText()

3

1

Token.getType()

3

1

Tokenizer.Tokenizer(String)

3

1

Tokenizer.nextToken()

51

25

Tokenizer.readNumber()

7

3

Tokenizer.skipWhitespace()

5

2

1.4 分析(方法层面)

整体来看,项目中方法的平均圈复杂度为 2.7,平均代码行数为 9.8 行。可深入数据后,发现以下几个方法严重超标:

危险方法 (Method)LOC (行数)v(G) (圈复杂度)缺点

Tokenizer.nextToken()

51

25

严重超标。方法内部堆砌了大量的 if-elseswitch-case 来判断字符。(但是比较省事)

 

 

Term.appendBody(...)

35

10

在控制多项式输出格式时,为了处理省略前导符号、系数为 1 等边界情况,导致 if 嵌套过多。

 

Parser.parsePrimary()

22

5

在处理基本因子时逻辑分支过多。

 

Polynomial.isSingleFactor()

13

6

逻辑判断条件略微复杂,包含过多复合布尔表达式。

优点:

  • 内聚性:在所有核心类中,绝大多数类的 LCOM 都接近于 1

  • 多态的运用:各类 AST 节点(AddNode, MulNode, ExpNode 等),它们的方法代码行数极短(均在 15 行以内),且圈复杂度基本为 1。


2.架构设计

以下为第三次作业(HW3)的类图:

2.1 每个类的设计考虑

流水线:词法分析 -> 语法解析 -> 抽象语法树(AST) -> 计算求值。

  1. 词法分析 (Tokenizer, Token, TokenType)

    • 将字符流转化为具有特定语义的 Token 单元。TokenType 枚举规范化了所有的输入种类。

  2. 语法解析 (Parser)

    • 实现了递归下降解析器。

  3. 抽象语法 (ExprNode 接口及其实现类)

    • 定义了统一的 ExprNode 接口。

  4. 输出 (Polynomial, Term)

    • Term (单项式):负责处理单项式的乘法和合并。

    • Polynomial (多项式):内部维护一个 Term 的集合(通常使用 HashMap 加速同类项合并),对外提供加减乘等代数运算接口。

2.2 架构的优点与缺点自我点评

  • 优点

    • 职责划分清晰与易扩展性:解析阶段(构建 AST)与求值阶段(多项式运算)完全解耦。AST 节点面向 ExprNode 接口编程,在第三次作业新增求导算子和函数调用时,我只需新增 DeriveNodeFuncCallNode 即可。

  • 缺点

    • 部分方法过度臃肿:正如度量分析指出的,Tokenizer.nextToken() 的圈复杂度过高。


3. 架构设计体验

3.1三次迭代:

  • HW1:确立递归下降解析,用HashMap处理单变量x的基础运算。

  • HW2:引入的指数与嵌套函数,因而采用Term和Polynomial组合的模式。

  • HW3:与HW2在架构没太大的差别。

3.2新的迭代情景:

  • 可以引入三角函数 sin() 和 cos(),并且允许其内部嵌套任意复杂的多项式或指数函数。
  1. 在TokenType中新增sin()和cos()枚举。
  2. 新增SinNode和CosNode实现统一的ExprNode接口。
  3. 在原有的 `Term`(单项式)类中,新增 `List<Polynomial> sinPolys` 等属性来存储三角项。

4. 代码中的Bug

  • HW1: parser类再读取指数时,应可以读取多重的“+”,但那时候忽略了这个细节。

  • HW2: 指数类型若是为int的话,范围根本不够,应为BigInteger(没有仔细读指导书的后果)


5. 互测

5.1 发现别人程序 Bug 所采用的策略

  • 本地评测机测试:利用 Python 编写脚本自动生成大规模随机测试数据,除了可以测试自己的代码也可以顺便测试别人的。

  • AI测评机:由于本地评测机有时候生成的随机数据范围较小,因此有时候会直接把AI当测评机来用,从而找出自己程序中的漏洞或者是用来分析有可能存在的bug或是忽略的细节,并把从自己这发现的漏洞直接用在他人的程序上。(同一个房间往往忽略的细节或bug都有相似性)


6. 架构优化与重构权衡

6.1 进行了什么优化?

  • HW1: 实现了正项提前(避免输出首项的 - 号前缀),并省略了所有无用的 1*^1 部分。

  • HW2 & HW3:主要实现 exp() 相关的深度优化。

    • 内部简单时:若 exp 内部只有一项,尝试去括号消解。

    • 内部复杂时:尝试通过提取公因数至外部来缩短整体长度。

6.2 优化能否保证简洁性与正确性?

结论是:过度优化不仅不能保证代码的简洁性,反而极容易反噬正确性与时间性能。

6.3 解决方案与反思

  • 适度优化“适度优化益脑,过度优化伤身”。时间是瓶颈的概率远远比空间高,尽量不要用极其复杂的时间复杂度去换取一点点空间的输出长度缩减。


7. 大模型使用情况分析

7.1 代码生成的 AI 使用率与效果

  • 第一次作业:一个暑假没碰代码遗忘了许多 Java 语法。因此主要把AI用于框架的构建,至于内部方法都是独立编写的,直接使用 AI 代码的比率较低。

  • 第二次与第三次作业:新加功能模块我通常都是先大概写一遍然后直接丢给AI优化,写的好的话就没差,写不好的话就有AI帮我改写。(整体 AI 使用率 ~40%)

7.2 其他部分的 AI 辅助应用

  • 评测机搭建:非常依赖 AI。我的本地评测机主要是 AI 生成的。AI 在生成合法测试数据上表现极佳,但在进行 Python 校验端时常出逻辑错漏,仍需人工调试。

  • 辅助找 Bug 与 Code Review:在一个功能完成后,我会让 AI 进行代码审查。但实践证明,AI 生成不出具有攻击性的测试用例,难以找出深层的架构 Bug。所以绝对不能认为 AI 的审查结果就一定正确。


8. 心得体会与未来方向

8.1 学习心得体会

说实话OO第一单元的难度还是挺高的,手搓代码的时间成本非常的高,但只有在自己手搓的过程中才能学到一些“自动化生成代码”(其实就是AI生成)学不到的东西。不是说“自动化生成代码”不好,只能说各自有各自的好处,只有两者并行,才能在这个AI高速发展的时代下,既不会与时代脱节,同时又能掌握基础技能。

除了学习方法以外,OO这门课的作业提交时间可能有点短的原因。因此莫名的让人会有种刺激感与上瘾(也有可能就是压力),彷佛无时无刻都想要搓一下代码。


9.未来方向

可能以后可以开一个互测大排名,也就是第一单元结束后就展出一个名单说谁在互测中成功hack别人次数最多,成功防御别人hack最多,谁的平均性能分最高等等然后再分享代码之类的?

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

302

社区成员

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

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