PTA--三次作业总结

一、前言

  1. 知识点覆盖
    基础语法:Java 类与对象、集合框架(ArrayList)、输入输出处理、精度控制(Math.round ())
    面向对象设计:单一职责原则(SRP)、类间关系(组合 / 聚合 / 依赖)、模块化封装
    业务逻辑:排序算法(冒泡排序)、多条件判断、重量 / 力矩 / 重心计算、数据校验
    工程实践:迭代开发、代码重构、测试用例设计、bug 定位与修复

2. 整体迭代思路
迭代 1:搭建基础框架,验证单场景核心流程(货物装载→排序→统计→判断)
迭代 2:扩展业务维度(多货舱),拆分职责(新增调度类、校验类),强化模块化
迭代 3:新增实体(旅客、行李),接入核心业务算法(重心计算),完善鲁棒性(数据校验)
二、设计与分析
(一)第一次作业:基础航班货运配载模块

  1. 类图设计

image

  1. 核心设计分析
    类职责拆分:
    Cargo:封装货物数据(名称、重量)
    Flight:负责航班业务流程(添加货物、排序、打印、重量计算、超载判断)
    Main:用来处理输入输出和对象初始化,不参与业务逻辑
    关键方法:
    sort ():冒泡排序核心代码(补充自身代码片段,说明双重循环逻辑)
    calcTotalWeight ():遍历 cargoList 累加重量,精度控制(补充精度处理细节)
    复杂度分析(补充自身 SourceMonitor 数据):
    | 类名 | 平均循环复杂度(OCavg) | 总循环复杂度(WMC) | 高复杂度方法说明 |
    |---------|--------------------------|--------------------|------------------|
    | Cargo | 1.0 | 2 | 无,仅数据封装 |
    | Flight | 3.5 | 14 | sort()(v(G)=6):双重循环;checkOverload()(v(G)=4):多条件判断 |
    | Main | 1.0 | 2 | 无,仅流程调用 |

  2. 设计心得
    初期困难:不熟悉多个类的结构设计代码,不能很好的结合多个类,并且用单一职责原则划分功能。
    关键收获:基本掌握SRP原理,明白开始从哪里下手设计代码,怎么分配任务,如何调动类和类。
    心得:设计开始要从简单的类开始设计,把基础打好,比如这道题目就应该先设计Cargo然后依次设计货舱和航班类。再按照要求设计Main类。
    (二)第二次作业:多货舱管理与重量排序装载

  3. 作业要求回顾
    核心功能:多货舱管理、货物定向装载、货舱 + 航班双维度超载判断
    设计约束:保持 SRP,新增调度类,输入校验类

  4. 类图设计(补充自身类图,标注类间关系)
    image

  5. 核心设计分析
    新增类职责:
    CargoCompartment:货舱实体,管理自身装载状态(货物列表,当前重量),实现 addCargo ()(内置判断自身超载)
    LoadDispatcher:工具类,专注货物排序(按重量降序)、查找,解耦排序逻辑与业务类
    InputValidator:预留校验接口,为第三次作业全量校验做铺垫
    关键方法优化:
    loadCargo ():按排序后的货物列表,定向匹配目标货舱,调用货舱 addCargo () 判断装载结果
    printFlightStats ():同时统计货舱总重量、航班总重量,对比最大起飞重量 / 最大业载重量

高复杂度方法说明

类名 平均循环复杂度(OCavg) 总循环复杂度(WMC) 高复杂度方法说明
CargoCompartment 2.0 8 addCargo()(v(G)=3):超载判断;getCurrentWeight()(v(G)=4):遍历统计
LoadDispatcher 2.5 5 sortCargos()(v(G)=5):冒泡排序
Flight 4.0 24 loadCargo()(v(G)=7):货舱匹配+装载判断;printFlightStats()(v(G)=6):多维度对比
InputValidator 1.0 3
Cargo 1.0 2
Main 1.0 2
  1. 设计心得
    类拆分的价值:将货舱管理拆分为独立类以后,Flight 类不需要再关注货舱内部逻辑,仅负责整体流程调度,可维护性提升
    (三)第三次作业:配平计算与全维度数据校验

  2. 作业要求回顾
    核心功能:旅客 + 行李管理、载重平衡计算(总重量 / 总力矩 / 重心 /% MAC)、全量数据校验(非法输入终止程序)

  3. 类图设计(补充自身类图,标注新增类关系)
    image

  4. 核心设计分析
    新增类职责:
    Luggage:封装行李重量,没有其他逻辑(组合关系:Passenger 创建时内部 new Luggage)
    Passenger:管理旅客行李,提供 getTotalWeight ()(75kg + 行李重量)
    WeightBalanceCalculator:纯工具类,依赖 Flight 对象,实现配平计算全流程(无 Flight 成员变量,通过方法传入)
    InputValidator:完善校验方法,支持非负校验、范围校验,非法输入直接终止程序
    关键算法实现:
    总重量计算:空机重量(40000kg)+ 旅客总重量 + 货舱总重量
    总力矩计算:空机力矩(40000×16.25)+ 旅客力矩(总重 ×18.0)+ 货舱力矩(前舱 ×12.0 / 后舱 ×22.0)
    重心百分比:((实际重心 - 15.0)/5.0)×100%(实际重心 = 总力矩 / 总重量)
    | 类名 | 平均循环复杂度(OCavg) | 总循环复杂度(WMC) | 高复杂度方法说明 |
    |-----------------------|--------------------------|--------------------|------------------|
    | WeightBalanceCalculator | 5.2 | 26 | generateLoadSheet()(v(G)=12):串联全计算流程;calcCGPercent()(v(G)=8):多步骤公式 |
    | InputValidator | 3.0 | 9 | getRangeInt()(v(G)=6):多条件校验+异常终止 |
    | Flight | 4.5 | 36 | printLoadSheet()(v(G)=10):复杂格式输出;validateInput()(v(G)=9):全量输入校验 |
    | CargoCompartment | 2.2 | 11 | addCargo()(v(G)=4):重量+位置双重判断 |
    | LoadDispatcher | 2.8 | 7 | bubbleSort()(v(G)=6):冒泡排序 |
    | Passenger | 1.0 | 3 | 无,仅数据封装 |
    | Luggage | 1.0 | 2 | 无,仅数据封装 |
    | Cargo | 1.0 | 2 | 无,仅数据封装 |
    | Main | 1.0 | 2 | 无,仅流程调用 |

  5. 设计心得
    设计思路:通过 "纯工具类 + 依赖注入"(如 WeightBalanceCalculator 传入 Flight)实现解耦,避免类间强关联
    细粒度拆分的重要性:Luggage 与 Passenger 的拆分,使每个类职责单一,后续修改行李计算规则时不需要改动 Passenger 核心逻辑
    三、采坑心得
    (一)第一次作业:基础功能实现坑

  6. 排序算法 bug
    冒泡排序时忽略 "同等重量按输入顺序排列" 要求,导致测试用例中同等重量货物顺序错乱,
    比如输入货物重量 [500, 850, 500],排序后应该输出 [850, 500, 500](正确),代码输出 [850, 500(后输入), 500(先输入)]
    冒泡排序时仅判断weight >,未处理weight ==时保持原顺序将排序条件改为 `weight 保持稳定排序

  7. 精度丢失问题
    计算总重量时,多个 double 类型累加导致精度偏差(如 500.1+850.2=1350.2999999999997)
    导致超载判断时出现误判(如总重量 1350.3,最大载重 1350.3,因精度偏差判定为超载)
    决定使用Math.round(totalWeight * 10) / 10.0保留 1 位小数累加 [500.1, 850.2, 128.3],
    修复前 1350.5999999999997,修复后 1350.6
    (二)第二次作业:多类协作与逻辑坑

  8. 货舱匹配逻辑漏洞
    货物目标货舱 ID 输入错误(如输入 "中舱",但仅存在 "前舱"" 后舱 "),程序直接抛出空指针异常
    前舱 5000 2 5;后舱 8000 3 5电子产品 850 中舱抛出 NullPointerException
    分析后发现loadCargo () 方法中没有判断目标货舱是否存在,直接调用货舱 addCargo ()
    添加货舱存在校验,如果不存在则输出 "装载失败:目标货舱不存在"

  9. 多货舱重量统计错误
    计算航班总重量时,仅统计了最后一个货舱的重量,忽略其他货舱
    是因为循环遍历货舱列表时,未累加重量,直接覆盖变量
    修复方案改为累加逻辑(totalWeight += compartment.getCurrentWeight();)
    (三)第三次作业:复杂逻辑与校验坑

  10. 数据校验不全面
    没有校验 "前舱装载货物件数 p 超出货物总件数 m"(如 m=3,p=4),导致数组越界
    导致输入非法 p 值时程序崩溃
    解决方案是在 InputValidator 中新增getRangeInt(sc, min, max)方法,限制 p 的范围为 [0, m](补充代码片段)
    2.鲁棒性检验不通过
    题目要求在输入货物重量的时候及时检测是否有超载现象,如果有立刻跳出提示并且终止程序
    初期没有发现这个问题导致检查点一直不过,最后在添加货物的时候自动检测是否超出货舱的最大容量
    然后直接System.exit(0);
    3.范围校验不通过
    十分抽象的一点是题目明明要求输入的到前舱的货物是0<=p,即大于零的整数,但是实际测试点要求是大于1的整数
    苦思良久还是没有找到这个问题,最后与通过了的同学沟通后才知道
    调整范围后直接通过了
    4.排序问题
    本次题目无需排序,排序反而会导致错误,普通测试点一直没有过,因为题目要求按照输入顺序来排列货物
    由于前两题都需使用排序而本题目么有声明,甚至提醒用冒泡排序,但是实际对货物进行排序会导致错误,
    由于测试样例太少,我始终没有发现这个情况,最后任然是与同学沟通才找到问题
    四、改进建议
    (一)代码层面改进

  11. 第一次作业改进
    排序算法优化:将冒泡排序改为选择排序,减少交换次数(时间复杂度仍为 O (n²),但实际效率提升)
    新增工具类:将Math.round()精度控制逻辑抽离为NumberUtil.round1(double num),复用性提升
    代码注释:为 sort ()、calcTotalWeight () 添加详细注释,说明算法逻辑与参数含义

  12. 第二次作业改进
    货舱查找优化:将 Flight 类中 "遍历查找目标货舱" 改为 HashMap 存储(key = 货舱 ID,value = 货舱对象),查找时间复杂度从 O (m) 降至 O (1)(补充改进后代码片段)
    装载结果缓存:将货物装载结果(成功 / 失败)存储在 List 中,最后统一输出,避免边装载边输出导致的顺序混乱
    异常处理:使用 try-catch 捕获输入非数字异常(如输入 "abc" 作为重量),提升鲁棒性

  13. 第三次作业改进
    计算结果缓存:WeightBalanceCalculator 中缓存总重量、总力矩等中间结果,避免重复计算(补充代码片段)
    配置常量抽离:将空机重量、力臂参数、MAC 参数等抽离为Config工具类,后续修改时无需改动业务代码
    输出模块化:将配载舱单输出逻辑抽离为LoadSheetPrinter类,Flight 类仅负责数据提供,符合 SRP
    (二)设计层面改进

  14. 类职责进一步拆分
    第三次作业中 Flight 类仍承担 "数据管理 + 流程控制 + 输出" 职责,可拆分为:
    FlightData:管理航班、旅客、货舱数据(纯数据类)
    FlightService:负责业务流程(装载、校验)
    FlightPrinter:负责结果输出
    改进后类职责更单一,可维护性更强

  15. 接口复用(后续作业可尝试)
    若取消 "无 interface" 约束,可定义Loadable接口(addLoad(Object obj)、getCurrentWeight()),让 CargoCompartment、Passenger 实现该接口,统一重量统计逻辑

  16. 测试友好性改进
    为核心类添加 setter 方法(如 Flight.setPassengers ()),便于单元测试时注入测试数据
    新增TestDataGenerator类,生成随机测试用例(如随机货物重量、旅客行李重量),提升测试效率
    (三)业务功能扩展

  17. 批量操作功能
    支持批量导入货物 / 旅客数据(从文件读取),减少手动输入

  18. 配载优化建议
    当货舱超载时,自动推荐货物转移方案(如将后舱超载货物转移至前舱)

  19. 日志记录
    新增日志类,记录装载时间、操作人员、货物信息等,便于追溯
    五、总结

  20. 学习收获
    (1)技术能力提升
    Java 基础:熟练掌握类与对象设计、集合框架使用、输入输出精度控制
    面向对象思想:深刻理解单一职责原则的实际价值,学会通过类拆分降低复杂度
    业务实现:具备从 "需求文档" 到 "代码实现" 的转化能力,能处理复杂公式与多条件逻辑
    问题解决:掌握 "复现 bug→定位原因→修复验证" 的完整流程,累计修复 20 + 各类 bug
    (2)工程实践认知
    迭代开发:体会到 "小步快跑、逐步迭代" 的优势,每次迭代仅聚焦少量新增功能,降低开发难度
    代码重构:认识到初期设计缺陷的必然性,重构是提升代码质量的关键步骤(三次作业累计重构 3 次)
    测试重要性:自动化测试与手动测试结合能大幅提升代码正确性,第三次作业通过 100 + 测试用例验证

  21. 待改进与深入学习的方向
    (1)技术层面
    算法优化:学习更高效的排序算法(如快速排序),应对更大数据量(如 1000 + 件货物)
    设计模式:学习工厂模式、策略模式,应用于复杂对象创建与算法切换(如不同配载策略)
    异常处理:系统学习 Java 异常体系,区分运行时异常与受检异常,提升代码鲁棒性
    (2)业务层面
    航空配载知识:了解更多航空配载规则(如重心范围的实际意义、危险品装载限制)
    性能优化:针对大规模数据(如 1000 + 旅客、10000 + 货物)优化代码,降低时间与空间复杂度

  22. 课程与作业建议
    提供测试用例:多提供一些官方测试用例,不求覆盖所有提到的输出情况,但至少要把特殊情况列举全面
    对于作业三,测试用例只有一个,而反常的部分较多,没有输入错误的时候的测试用例,导致做题时没有办法确认代码
    是否输入正确,以及题目没有特别通知某一个变量的范围,导致我浪费大量不必要的时间

  23. 总结感悟
    三次作业从简单的单货舱装载到复杂的载重平衡计算,不仅是技术能力的提升,更是工程思维的培养。通过本次学习,我深刻认识到 "好的设计比好的代码更重要"—— 遵循单一职责原则、合理拆分类职责,能让代码在迭代过程中保持清晰与可维护性。同时,bug 修复的过程让我学会了严谨思考,每一个小问题背后都可能隐藏着设计或逻辑的漏洞。
    未来,我将继续深入学习设计模式与性能优化,提升代码的复用性与效率;同时,更加注重业务理解,让技术更好地服务于实际需求。本次作业的经历也为后续复杂系统开发积累了宝贵经验,将受益良多。

posted @ 2026-05-18 15:41  25202124胡雨峰  阅读(10)  评论(0)    收藏  举报