面向对象程序设计课程作业集1~3总结

一 、前言

  本次题目集共包含三次编程任务,难度逐次递增,覆盖了从基础类的设计与封装到多类协作、算法实现,再到引入航空载重平衡计算的专业场景。三次作业的知识点、题量与难度总结如下:

题目集 核心知识点 题量 难度
第一次 类与对象、封装、简单排序(选择排序)、数组操作、基本输入输出 中等(约120行) 中等
第二次 多类协作、集合框架(ArrayList)、组合关系、输入验证、多个货舱管理 较大(约200行) 较难
第三次 组合/依赖关系设计、重量与平衡计算(力矩/重心/百分比MAC)、冒泡排序、严格的类职责分离 大(约300行)

  

二、设计与分析  

 1.  第一次作业

  • 整体结构概览

     第一次作业实现了基本的货物配载模拟程序,包含以下5个类:

类名 职责 方法数
Cargo     货物实体,记录名称和重量 3
Flight 航班实体,记录航班号与最大载重 2
CargoSorter 货物排序工具类(选择排序) 1
LoadManifest   配载管理,计算总重量并判断超载 2
Main  程序入口,处理输入输出及流程调度

1

    

  • 类图

      PowerDesigner所生成的类图如下:

image

     类间关系说明:

        1. LoadManifest 关联 Flight 和 Cargo:配载单需要知道航班的最大载重,并管理一批货物的重量信息。

        2. CargoSorter 依赖 Cargo:排序工具类直接操作货物数组,通过调用货物的 getter 方法进行比较。

        3. Main 协调所有类:负责创建对象、调用排序和配载方法、处理输入输出,是程序的调度中心。

  • 解读

     SourceMonitor所生成报告如下:

      image

     关键数据指标及解读: 

       1. 最大复杂度:4(出现在 CargoSorter.sort()。圈复杂度≤10为健康,4表示该方法有4条独立路径(两层循环+一个if),逻辑清晰可接受。

       2. 平均每方法语句数:4.08(方法体较小,职责单一。但 Main.main() 有17条语句,远超平均值)

       3. 最大块深度:5(位于 sort() 最内层 if。深度≥5会降低可读性,可考虑将内层循环提取并修改为独立方法。)

       4. 注释百分比:11.7%(偏低。关键算法(如选择排序)缺少注释,不利于团队协作和维护。

 

  • 设计心得

  • 亮点
  • 类职责初步分离,满足单一职责;
  • 排序算法独立为工具类,便于复用;
  • 构造器内深拷贝数组,保护内部状态。
  • 待优化点:
  • 优化装载逻辑:修改load()方法,按排序后顺序装入,一旦超重则跳出循环,并输出未装载货物列表;
  • 提升注释覆盖率:将代码注释率提高到20%以上并为关键方法(如排序算法)添加块注释,说明算法思路;
  • 拆分Main方法:将输入读取、输出显示分别拆分为readInput()和printResult(),降低复杂度;
  • 创建封装输入工具:创建InputHelper类,提供readLine()、readDouble()等安全方法,统一处理换行符;

 

 

 2.  第二次作业

  • 整体结构概览

     第二次作业在第一次基础上增加了货舱管理、货物按舱位装载、多货舱容量控制等功能,共包含 7 个类,结构更加完整。具体如下:

类名 职责 方法数
Cargo 货物实体,记录ID、重量及指定货舱ID 4
Position 货舱位置抽象(行、列、所属货舱) 2
CargoCompartment 货舱实体,管理最大载重及舱内货物列表 6
Flight  航班实体,管理多个货舱及整体重量限制 7
LoadDispatcher 货物排序(选择排序)及查找工具 2
InputValidator   输入验证工具(数值范围检查) 2
Main  程序入口,处理输入、装载、输出全流程

1

    

  • 类图

      PowerDesigner所生成的类图如下:

image

     类间关系说明:

        1. Flight 聚合多个 CargoCompartment(组合关系)

        2. 每个 CargoCompartment 包含多个 Cargo 和多个 Position

        3. LoadDispatcher 作为工具类操作 Cargo 列表

          4.InputValidator 为纯静态方法工具类

 

  • 解读

     SourceMonitor所生成报告如下:

      image

     关键数据指标及解读: 

       1. 最大复杂度:15(出现在 Main.main()。圈复杂度 15 已超出健康阈值(10),说明 main 方法分支过多(包含多个循环、条件判断和输出分支),逻辑复杂,难以测试和维护。)

       2. 平均每方法语句数:4.50(大部分方法体较小,职责较单一。但 Main函数中语句数远超平均值(估算约 40~50 条),是主要超标项。

       3. 最大块深度:5(位于 sortCargos() 最内层 if 或 main 中的循环嵌套。深度 5 尚可接受,但结合高复杂度仍需改进。

       4. 注释百分比:3.4%(极低。关键逻辑(如装载判断、排序算法)完全没有说明,严重影响可读性和团队协作。)

       5.平均复杂度:2.12(整体健康,但被 main 的 15 拉高

       6.分支语句占比 :15.4%(略高,主要来自 main 中的多重 if-else 判断。

 

  • 设计心得

  • 亮点
  • 货舱容量实时检查:CargoCompartment.addCargo() 在添加前判断是否超载,并返回 boolean 告知调用方;
  • 按舱位查找装载:Flight.findCompartmentById() 支持根据货物指定的货舱 ID 动态定位目标货舱,体现了面向接口的查询思想;
  • 排序独立复用:LoadDispatcher.sortCargos() 使用选择排序实现降序排列,与第一次作业保持一致,方便复用;
  • 输入验证初步引入:InputValidator 提供了数值范围校验的静态方法,虽然本次未在 main 中强制使用,但为后续安全输入奠定了基础。

 

  • 待优化点:

  • Position 类完全冗余:直接删除 Position 类及 CargoCompartment 中的 positions 列表,除非后续需要具体位置管理
  • 注释覆盖率极低(3.4%):为核心类和关键方法添加块注释,说明算法思路、参数含义及返回值约定,使得目标注释率 ≥20%;
  • InputValidator 未被使用:在读取 nextDouble()、nextInt() 后立即调用 validateDouble/Int,若非法则提示并重新输入(或退出);
  • Main.main() 复杂度过高(15):拆分为 readInput()、loadCargos()、printStatus() 等子方法,降低圈复杂度至 ≤5。

 

 

 3.  第三次作业

  • 整体结构概览

     第三次作业在前两次基础上,增加了旅客与行李管理以及航空器载重平衡计算功能,系统总类数达到 11 个,结构更加完整且贴近真实业务场景。具体如下:

类名 职责 方法数
Cargo  货物实体,记录ID、重量及指定货舱ID 4
Position 货舱位置抽象(行、列、所属货舱) 2
CargoCompartment 货舱实体,管理最大载重及舱内货物列表 5
Flight   航班实体,管理多个货舱、旅客列表及空机重量 9
Passenger 旅客实体,管理姓名、标准体重及行李(组合) 3
Luggage  行李实体,记录行李重量 2
WeightBalanceCalculator 载重平衡计算工具(力矩、重心、%MAC) 1(内嵌Result类)
LoadDispatcher 货物排序(冒泡排序降序)及查找工具 2
InputValidator  统一输入验证与获取(含超载检查) 7
Main  程序入口,处理输入、装载、平衡计算与输出

1

    

  • 类图

      PowerDesigner所生成的类图如下:

image

     类间关系说明:

        1. Flight 聚合 CargoCompartment 和 Passenger(组合关系),体现航班包含多个货舱和多名旅客;

        2. Passenger 组合 Luggage(严格组合:行李对象在 Passenger 构造器内部 new,不对外暴露),符合设计要求;

        3. WeightBalanceCalculator 依赖 Flight:通过 generateLoadSheet(Flight flight) 方法传入 Flight 对象进行纯计算,无成员变量存储 Flight,符合依赖关系设计;

          4. LoadDispatcher 作为工具类操作 Cargo 列表,排序算法采用冒泡排序(降序),不使用 Lambda 或 Collections.sort();

       5. InputValidator 提供统一的静态输入方法,主流程通过它读取所有数据并进行合法性检查(负数、范围等),非法输入直接 System.exit(0)。

 

  • 解读

     SourceMonitor所生成报告如下:

      image

     关键数据指标及解读: 

       1. 最大复杂度:17(出现在 Main.main()。圈复杂度 17 已超出健康阈值(≤10),说明 main 方法分支极多(包含循环、多重条件判断、嵌套输出逻辑),难以测试和维护。)

       2. 平均每方法语句数:5.27(大部分方法体较小,但 Main.main() 语句数高达 84,严重拉高平均值

       3. 最大块深度:5(位于 sortCargos() 最内层交换逻辑 或 main 中的循环嵌套。深度 5 尚可接受,但结合高复杂度仍需改进。

       4. 注释百分比:4.1%(极低。虽然部分类(如 Passenger、Luggage、WeightBalanceCalculator)有简要注释,但核心算法(冒泡排序、力矩计算)和复杂输入逻辑缺少说明,严重影响可读性。)

       5.平均复杂度:2.16(整体健康,但被 main 的 17 拉高。

       6.分支语句占比 :14.2%(较高,主要来自 main 中的多重 if-else 和循环内的条件判断

 

  • 设计心得

  • 亮点
  • 严格遵循单一职责原则:Passenger 与 Luggage 分离,WeightBalanceCalculator 独立为纯计算类,InputValidator 统一输入处理,类职责清晰;
  • 正确的组合关系:Passenger 内部创建 Luggage 对象,不对外暴露修改,体现了封装性和对象生命周期的管理;
  • 算法合规:排序使用冒泡排序(降序),满足“不允许使用 Lambda 及 Collections.sort()”的要求;
  • 输入鲁棒性增强:InputValidator 对负数、范围进行校验,非法输入立即终止程序,满足题目“增强鲁棒性要求”。

 

  • 待优化点:

  • Main.main() 复杂度过高(17)且语句数过多(84):拆分为多个子方法:readFlightAndCompartments()、readPassengers()、readCargos()、performLoading()、printBalanceSheet(),将每个子方法的圈复杂度降至 ≤5;
  • 注释覆盖率极低(4.1%):为 WeightBalanceCalculator 中的每个常量添加注释说明来源;为冒泡排序添加算法思路注释;为 main 中的关键步骤添加块注释使得整体注释率达到20%及以上;
  • Position 类完全冗余:直接删除 Position 类及 CargoCompartment 中的 positions 列表,除非后续需要具体位置管理;
  • 超载处理过于激进:改为返回 boolean 或抛出可捕获的异常,在 main 中提供重试或跳过逻辑,而不是直接终止整个程序。

 

 

三、踩坑心得

坑点1:货物排序算法中使用冒泡排序无法得到正确的排序结果(V1出现)

问题如图所示:

image

解决方法:

重新编写排序方法,使用多种排序方法测试最终结果是否合格。最终得到排序方法有俩种方案可合格:

方案一:使用冒泡排序进行逆向排序再将列表或数组进行倒转,下图为方案一用时与内存:

image

 

方案二:使用选择排序进行排序可直接得到答案,下图为方案二用时与内存:

image

由于方案一与方案二时间复杂度与内存差距不大,故在代码长度的考虑下使用方案二进行排序。

 

心得:

当需要对某类进行排序时可从简单排序开始尝试,利用时间复杂度、排序效果和代码长度等进行筛选,找出最符合题目要求的排序方法。

 

坑点2:货物超载判断时使用的条件出错导致无法出现正确结果(V2出现)

问题如图所示:

image

解决方法:

将判断条件修改即可

image

心得:

当出现多重判断时我们要注意判断条件从而实现正确的输出结果,其次我们还可以通过简化判断条件来减少错误出现的情况。

 

坑点3:输入数据时的合法性判断错误(V3出现)

问题如图所示:

image

解决方法:该代码判断p是否在最小值0与最大值m之间,但实际生活中p的最小值应为1;将0修改为1即可。

 

心得:

代码作业中的测试点以及测试数据会模拟实际用户的需求,即实际用户的输入是不可预测的,没有“公开数据”供我们使用,所以我们需要养成“深挖需求、严谨设计边界”的工程习惯。

 

坑点4:数组排序后与输入时的顺序不同导致输出结果错误(V3出现)

问题如图所示:

image

解决方法:

根据题目输出预测输出应为根据货物ID顺序输出,而排序后货物下标与ID不符导致输出错误,只要加入判断条件即货物ID与下标相同的输出即可,如图所示:

image

心得:

当有排序需求时我们要关注输出内容中有没有与排序数组有关的信息,我们要根据信息修改输出循环或输出条件使得排序后的数组满足输出条件。

 

四、改进建议

4.1 第一次作业改进建议

核心问题:装载逻辑过于暴力、输入处理不够完善、注释不足。

改进方案:

  1. 优化装载逻辑:

修改 LoadManifest.load() 方法,改为逐个货物尝试装载:若当前累计重量 + 下一货物重量 ≤ 最大载重,则装入并累加;否则跳出循环,并记录未装载货物。最后输出成功装入的货物列表和因超重未装载的的货物列表。

  2. 拆分 Main 方法

将 main 中的输入、排序输出、装载、结果打印分别提取并分为 readInput()、printSortedCargos()、executeLoading()、printManifest() 四个私有方法,降低圈复杂度

  3. 封装输入工具:

创建 InputHelper 类,提供 readLine()、readDouble()、readInt() 等方法,统一处理 Scanner 的换行残留问题,避免在 main 中反复调用 sc.nextLine()。

  4. 使用 List 替代数组:

将 Cargo[] 改为 List<Cargo>,便于动态增删货物,并为后续多货舱扩展做准备。

 

4.2 第二次作业改进建议

核心问题:Position 类冗余、main 复杂度过高(15)、输入验证未实际使用、注释率极低(3.4%)。

改进方案:

  1. 删除冗余的 Position 类

如果不需要记录货物在货舱中的具体行列位置,直接删除 Position 类以及 CargoCompartment 中的 positions 列表。

  2. 降低 main 方法复杂度

将 main 拆分为至少 5 个子方法:readFlightInfo()、readCompartments()、readCargos()、loadCargosToCompartments()、printLoadingReport(),使每个方法圈复杂度 ≤5

  3. 实际使用 InputValidator:

在读取 nextDouble()、nextInt() 后立即调用 validateDouble/validateInt,若非法则提示用户重新输入或退出。可扩展 InputValidator 提供 getValidatedDouble(String prompt, double min) 等方法,主动循环直到合法。

  4. 优化装载失败处理:

当某个货物因货舱容量不足而装入失败时,除了打印失败信息,还应增加失败计数器并累计次数,最后汇总输出失败货物 ID 列表,便于用户调整。

 

4.3 第三次作业改进建议

核心问题:Main.main() 复杂度过高(17)且语句数达 84,注释率仍低(4.1%),超载处理过于激进(直接 System.exit(0)),货物输出逻辑冗余。

改进方案:

  1. 彻底拆分 main 方法

按实际需求阶段拆分:readFlightBaseInfo()、readCompartmentsConfig()、readPassengerLuggage()、readCargoAssignment()、performCargoLoading()、generateAndPrintBalanceSheet()。每个方法负责一件事,主方法仅保留调用序列。

  2. 优化超载处理逻辑

将 InputValidator.checkFrontOverload/checkRearOverload 改为返回 boolean 或抛出业务异常,在 main 中捕获后提供两种选择:①跳过当前货物继续装载后续货物;②让用户重新输入该货物重量。避免直接 System.exit(0) 导致程序崩溃

  3. 删除无用代码:

LoadDispatcher.FindCargo 方法从未被调用,应删除。同时检查 CargoCompartment 中是否还有其他未使用的方法或属性。

  4. 增加常量注释与公式推导:

在 WeightBalanceCalculator 中为每个常量(如 BASE_ARM、MAC_LENGTH)添加注释说明其来源及物理意义;为 generateLoadSheet 中的力矩和重心计算公式添加推导注释,方便他人理解和维护。

 

五、总结

5.1 学到了什么

  1.面向对象分析与设计能力

  • 从第一次作业的简单类封装,到第二次的多类协作,再到第三次严格遵循单一职责、组合关系、依赖关系,我逐步理解了如何将现实业务抽象为类的基础模型和增殖模型。
 
  • 学会了辨别“组合”与“关联”的区别(如 Passenger 组合 Luggage,Flight 关联 CargoCompartment)。

 

  2.算法手动实现

  • 分别实现了选择排序(第一次/第二次)和冒泡排序(第三次),加深了对循环、交换、时间复杂度的理解。

  • 在没有现成库函数的情况下,能够独立编写排序逻辑。

 

  3.代码度量与质量意识

  • 使用 SourceMonitor 分析圈复杂度、块深度、注释率等指标,学会了用数据驱动代码重构。

  • 认识到“高复杂度”和“低注释率”是代码腐化的前兆,必须在编码过程中主动避免。

  

  4.专业领域建模

  • 学习了航空载重平衡中的基本概念:空机重量、力臂、力矩、重心(CG)、平均空气动力弦(MAC)、%MAC 和安全包线。

  • 能够将物理公式准确转换为代码,并保证计算精度。

 

5.2 需要进一步学习及研究的方向

  • 设计模式:当前代码仍偏“过程式”,可以引入工厂模式创建货物和旅客,策略模式切换排序算法,模板方法统一装载流程。
  • 异常处理机制:目前超载直接 System.exit(0) 过于粗暴,应学习并使用 try-catch 自定义业务异常使代码能够提供恢复机制。
  • 集合框架优化:findCompartmentById 线性查找效率低,可改用 Map<String, CargoCompartment> 实现更加快速,效率更高的查找。
  • 物理模型深化:研究真实航空器配载中重心包线、燃油消耗对重心影响、旅客座位分布等更复杂的平衡因素。

 

5.3 对课程、教师及组织方式的建议

  • 增加作业迭代的衔接说明,在每次作业发布时,明确列出本次在上一版基础上的新增点(比如第三次的“增量要求”),帮助学生快速定位变化;
  • 课堂补充设计原则实例,多讲解“单一职责”“组合优于继承”在真实项目中的正反例,结合本次作业中的 Passenger-Luggage 组合进行分析;
  • 减少网课量,多利用线下课程与学生零散时间完成和学习相关任务,而不是网课这种无用形式占用学生时间,如今优秀的网课在很多平台上都能观看,应该让学生自主选择观看网课而不是强制观看学校网课。
posted @ 2026-05-16 21:06  吴益睿  阅读(52)  评论(1)    收藏  举报