2026面向对象第一次博客作业

王新宇-24231208 2026-04-01 22:24:07

第一单元(表达式展开与化简)单元总结

一、基于度量分析程序结构

1.1 类的属性与方法统计

类名代码行数属性个数方法个数总代码规模
Expression4181 (monomials)18较大
Expression.Monomial143 (内部类)4 (coeff, xexp, yexp, expExpr)10中等
Parser~2303 (tokenizer, curToken, funcEnv)12较大
DerivativeFactor~1302 (expr, variable)5中等
FuncEnv~1276 (hasFunc, defExpr, hasRecursiveFunc, f0Expr, f1Expr, recursiveDef)7中等
Main~16004中等
其他Factor类平均~50平均2-3平均2-3较小

1.2 方法规模与控制分支分析

Expression类关键方法:

方法名代码行数控制分支数圈复杂度评价
toNecessaryBracketString()11 (重构后)2良好,已拆分
appendMonomial()101良好
mergeLikeTerms()394可接受
multiplyExpr()233可接受
replaceVar()304可接受

Parser类关键方法:

方法名代码行数控制分支数圈复杂度评价
parseExpression()152良好
parseFactor()2510需要关注,使用了switch-case
parseTermExpr()122良好

DerivativeFactor类关键方法:

方法名代码行数控制分支数圈复杂度评价
differentiateMonomial()24 (重构后)3良好,已拆分
differentiateVarAndExp()404可接受
differentiateVar()233可接受

1.3 OO度量分析

内聚性分析:

  • Expression类:高内聚,所有方法都围绕表达式的操作(加法、乘法、合并同类项等)
  • Monomial内部类:高内聚,封装了单项式的属性和基本操作
  • Factor层次结构:中等内聚,各Factor子类通过继承实现多态

耦合性分析:

  • Expression与Monomial:紧耦合,但属于合理范围(组合关系)
  • Factor与Expression:松耦合,通过asExpression()接口交互
  • Parser与其他类:中等耦合,负责创建各类对象
  • FuncEnv与Expression:松耦合,通过参数传递

设计模式应用:

  • 工厂模式:Parser类作为工厂,根据输入创建不同的Factor对象
  • 策略模式:Factor层次结构,不同类型的因子有不同的求值策略
  • 组合模式:Expression由Monomial组成,Monomial可以包含Expression(expExpr)

二、类图设计与自我点评

2.1 类图(文字描述)

┌─────────────────────────────────────────────────────────────┐
│                        Factor (抽象)                         │
│                      + asExpression()                       │
└─────────────────────────────────────────────────────────────┘
                              △
          ┌──────────────────┼──────────────────┐
          │                  │                  │
┌─────────┴─────┐  ┌─────────┴─────┐  ┌─────────┴─────┐
│ ConstFactor   │  │VariableFactor │  │  ExprFactor   │
│  - value      │  │  - var        │  │  - expr       │
│  - exponent   │  │  - exponent   │  │  - exponent   │
└───────────────┘  └───────────────┘  └───────────────┘
          │
          │          ┌───────────────┐  ┌───────────────┐
          │          │  ExpFactor    │  │ FuncCallFactor│
          │          │  - parameter  │  │  - actual     │
          │          │  - exponent   │  │  - funcEnv    │
          │          └───────────────┘  └───────────────┘
          │
          │          ┌───────────────┐  ┌───────────────┐
          └─────────▶│DerivativeFactor│  │ ChoiceFactor  │
                     │  - expr       │  │  - cond...    │
                     │  - variable   │  │  - ifTrue     │
                     └───────────────┘  │  - ifFalse    │
                                        └───────────────┘

┌─────────────────────────────────────────────────────────────┐
│                        Expression                           │
│  - monomials: List<Monomial>                                │
│  + addExpr() + multiplyExpr() + mergeLikeTerms()            │
│  + replaceVar() + toNecessaryBracketString()                │
├─────────────────────────────────────────────────────────────┤
│  ┌───────────────────────────────────────────────────────┐  │
│  │  Monomial                                             │  │
│  │  - coeff, xexp, yexp, expExpr                        │  │
│  │  + equals() + hashCode() + toString()                │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                          Parser                             │
│  - tokenizer, curToken, funcEnv                             │
│  + parseExpression() + parseFactor() + parseTerm()          │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                         FuncEnv                             │
│  - hasFunc, defExpr, hasRecursiveFunc                       │
│  - f0Expr, f1Expr, recursiveDef                             │
│  + getRecursiveFuncValue()                                  │
└─────────────────────────────────────────────────────────────┘

2.2 设计考虑说明

1. Factor层次结构

  • 设计考虑:将不同类型的因子抽象为Factor基类,通过多态统一处理
  • 优点:易于扩展新的因子类型,代码复用性高
  • 缺点:部分子类(如DerivativeFactor)逻辑较复杂,需要处理多种情况

2. Expression与Monomial组合

  • 设计考虑:Expression由多个Monomial组成,Monomial作为基本运算单元
  • 优点:便于合并同类项,支持复杂的表达式运算
  • 缺点:Monomial嵌套Expression(expExpr)增加了复杂度

3. Parser职责分离

  • 设计考虑:将解析逻辑集中在Parser类,通过递归下降法解析表达式
  • 优点:解析逻辑清晰,易于维护
  • 缺点:parseFactor()方法较复杂,需要处理多种因子类型

4. FuncEnv管理函数定义

  • 设计考虑:将函数定义和递推关系封装在FuncEnv中
  • 优点:支持普通函数和递推函数的统一管理
  • 缺点:递推函数的展开逻辑较复杂,需要递归计算

2.3 优点与缺点总结

优点:

  1. 良好的层次结构:Factor→Expression→Monomial的层次清晰
  2. 高内聚低耦合:各类职责单一,通过接口交互
  3. 易于扩展:新增因子类型只需继承Factor
  4. 重构友好:长方法已拆分为小方法,便于维护

缺点:

  1. 部分方法复杂度高:如parseFactor()的switch-case较多
  2. 递推函数展开效率:递归展开可能导致性能问题
  3. 表达式嵌套深度:expExpr嵌套Expression增加了复杂度

三、架构设计体验

3.1 三次作业迭代过程

第一次作业(基础表达式展开):

  • 实现了基本的表达式解析和展开
  • 架构:Parser → Expression → Monomial
  • 支持:加减乘、幂函数、常数

第二次作业(添加函数和指数):

  • 新增:自定义函数f(x)、指数函数exp(x)
  • 架构演进:引入Factor层次结构,FuncEnv管理函数
  • 挑战:函数调用的展开和参数替换

第三次作业(添加递推和求导):

  • 新增:递推函数f{n}(x)、求导因子dx/dy/grad
  • 架构演进:DerivativeFactor、递归展开逻辑
  • 挑战:递推函数的递归展开、链式求导法则

3.2 重构经历

重构前的问题:

  1. toNecessaryBracketString() 方法过长(~70行)
  2. parseRecursiveDef() 方法过长(~70行)
  3. differentiateMonomial() 方法过长(~65行)
  4. 命名不规范(如xExp应为xExponent

重构后的改进:

  1. toNecessaryBracketString()拆分为6个小方法
  2. parseRecursiveDef()拆分为4个辅助方法
  3. differentiateMonomial()拆分为3个专用方法
  4. 统一命名规范,提高代码可读性

重构体验:

  • 重构后代码更易读,每个方法职责单一
  • 测试覆盖率保证了重构的正确性
  • 后续添加新功能时更加便捷

3.3 新迭代情景的可扩展性

假设新迭代:添加三角函数sin/cos

可扩展性分析:

  1. 新增SinFactor/CosFactor类:继承Factor,实现asExpression()
  2. Parser扩展:在parseFactor()中添加sin/cos的case
  3. DerivativeFactor扩展:添加sin/cos的求导规则
  4. Expression扩展:无需修改,现有架构已支持

优势:

  • Factor层次结构使得新增因子类型简单
  • 求导逻辑已模块化,易于扩展
  • 表达式运算框架通用,无需改动

四、Bug分析

4.1 未通过的公测用例分析

Bug 1:递推函数系数提取错误

  • 特征f{n}(x)= 1 *f{n-1}(x) + 2 * f{n-2}(x) 计算结果错误
  • 问题类:Main.java的parseRecursiveDef()方法
  • 原因:系数提取逻辑未正确处理空格和乘号
  • 解决方案:改进系数提取算法,先跳过空格和乘号,再提取数字

Bug 2:变量替换不完全

  • 特征f{3}((x+y)) 展开后仍包含x而非(x+y)
  • 问题类:FuncEnv.java的getRecursiveFuncValue()方法
  • 原因:递推式中的参数未正确替换
  • 解决方案:确保递推式计算后统一进行变量替换

Bug 3:exp表达式括号不匹配

  • 特征:输出exp((x)缺少右括号
  • 问题类:Expression.java的toNecessaryBracketString()方法
  • 原因:括号添加逻辑有误
  • 解决方案:修复括号添加逻辑,确保成对出现

4.2 方法复杂度与Bug关系

方法代码行圈复杂度是否出Bug分析
parseRecursiveDef()70→30高→中复杂度高导致逻辑错误
getRecursiveFuncValue()25递归逻辑复杂
toNecessaryBracketString()70→11高→低重构后修复
parseFactor()25虽然复杂但逻辑清晰
mergeLikeTerms()39逻辑稳定

结论: 方法复杂度与Bug发生率正相关,重构拆分方法能有效减少Bug。

4.3 降低复杂度的方法

  1. 方法拆分:将长方法拆分为多个职责单一的小方法
  2. 提取公共逻辑:将重复代码提取为辅助方法
  3. 使用设计模式:如策略模式简化条件判断
  4. 增加注释:复杂逻辑添加详细注释

五、测试策略与发现Bug

5.1 测试策略

1. 单元测试

  • 对每个Factor子类进行独立测试
  • 测试Expression的基本运算(加减乘)
  • 测试边界情况(空表达式、零系数等)

2. 集成测试

  • 测试Parser解析完整表达式
  • 测试函数定义和调用
  • 测试递推函数的展开

3. 边界测试

  • 大指数(如x^100)
  • 嵌套函数(如f(g(x)))
  • 复杂递推(多层递归)

4. 随机测试

  • 生成随机表达式验证展开正确性
  • 对比不同展开顺序的结果一致性

5.2 测试有效性

  • 发现的Bug:系数提取、变量替换、括号匹配等问题
  • 未发现的Bug:部分复杂递推场景(后期通过互测发现)

5.3 结合代码设计测试用例

  • 针对Factor层次结构:测试每种类型的因子
  • 针对递推逻辑:测试不同n值的展开
  • 针对求导逻辑:测试各种函数的导数

六、优化分析

6.1 已做的优化

1. 方法拆分

  • 将长方法拆分为小方法,提高可读性
  • 提取公共逻辑,减少代码重复

2. 命名规范

  • 统一命名风格(camelCase)
  • 使用有意义的变量名

3. 表达式化简

  • 合并同类项,减少输出长度
  • 优化括号使用,避免冗余

6.2 优化的正确性保证

  • 测试覆盖:重构前后运行相同测试用例,确保行为一致
  • 边界测试:验证边界情况的处理
  • 性能测试:确保优化不引入性能问题

6.3 可能的进一步优化

1. 缓存机制

  • 缓存递推函数的计算结果,避免重复计算
  • 使用Memoization优化递归

2. 并行计算

  • 对独立表达式的求导并行处理
  • 利用多核CPU加速

3. 表达式简化

  • 更激进的化简策略(如因式分解)
  • 识别并简化常见模式

七、大模型使用评估

7.1 大模型辅助的场景

1. 代码生成

  • 生成基础的类结构和方法框架
  • 生成测试用例模板
  • 效果:良好,但需要人工检查和调整

2. Bug定位

  • 帮助分析错误信息和可能原因
  • 提供修复建议
  • 效果:中等,需要结合代码上下文

3. 重构建议

  • 提供方法拆分和命名建议
  • 效果:良好,但需根据实际需求调整

4. 文档撰写

  • 辅助撰写注释和文档
  • 效果:良好,提高了文档质量

7.2 大模型的局限性

  1. 上下文理解:对复杂业务逻辑理解有限
  2. 代码风格:生成的代码风格可能不一致
  3. 边界情况:对边界情况的处理不够完善

7.3 互测房间AI使用观察

观察到的现象:

  • 天枢星:代码中有大量详细的Javadoc注释,风格统一,疑似AI生成
  • 开阳星:使用了复杂的优化策略(如自动微分),正常人不太可能在此作业中使用

判断依据:

  1. 注释风格过于规范和统一
  2. 使用了超出课程范围的算法
  3. 代码结构过于完美,缺乏个人风格

八、心得体会

8.1 本单元学习收获

1. 面向对象设计

  • 深刻理解了继承、多态、封装的应用
  • 学会了如何设计可扩展的类层次结构

2. 递归与迭代

  • 掌握了递推函数的递归展开方法
  • 理解了递归与迭代的权衡

3. 代码质量

  • 认识到命名规范和代码风格的重要性
  • 学会了通过度量分析代码质量

4. 测试驱动

  • 体会到测试对保证代码正确性的重要性
  • 学会了设计有效的测试用例

8.2 遇到的挑战

  1. 递推函数的展开:理解递归展开和参数替换的逻辑
  2. 求导的实现:处理链式法则和乘法法则的复杂情况
  3. 表达式化简:合并同类项和优化括号使用

8.3 成长与反思

  • 架构设计能力:从"能跑就行"到"设计良好"
  • 代码质量意识:从"实现功能"到"高质量代码"
  • 问题解决能力:从"调试找错"到"分析预防"

九、未来方向建议

9.1 课程改进建议

1. 增加前置知识

  • 提前介绍设计模式和架构设计原则
  • 增加递归和树形结构的练习

2. 强化测试环节

  • 要求提交测试用例和覆盖率报告
  • 介绍单元测试和集成测试的方法

3. 代码评审环节

  • 增加同学间代码评审的环节
  • 提供优秀代码示例供学习

4. 迭代式开发

  • 明确每次作业的迭代目标和架构演进
  • 提供架构设计的指导和示例

9.2 对后续单元的期望

  • 希望继续强化架构设计能力
  • 期待更多关于设计模式的学习
  • 希望增加团队协作的开发体验

十、总结

第一单元的学习让我深刻体会到了良好架构设计的重要性。通过三次作业的迭代,我逐步构建了一个可扩展、易维护的表达式处理系统。虽然在过程中遇到了不少Bug和挑战,但正是这些问题让我学会了如何通过度量分析代码质量、如何进行有效的重构、如何设计全面的测试用例。

面向对象编程不仅仅是语法的使用,更是一种思维方式。通过合理的类设计和职责划分,我们可以构建出既满足当前需求又易于未来扩展的系统。这对我今后的软件开发生涯将产生深远的影响。


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

302

社区成员

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

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