航空器配载与货运管理系统三次作业总结

航空器配载与货运管理系统三次作业总结

一、前言

1.1 作业背景
本次系列作业围绕“航空器配载与货运管理系统”展开,共进行三次迭代开发。配载是航班起飞前的关键环节,需要计算飞机上旅客、货物、行李的重量分布,确定重心位置,确保飞行安全。三次作业从基础的货运装载开始,逐步增加了多货舱管理、旅客管理、重心计算等功能。

1.2 知识点分布
作业次序 核心知识点 新增技术点
作业一 类与对象、封装、数组 工具类设计、物理公式计算
作业二 组合关系、聚合关系、列表 插入排序、输入校验框架
作业三 工具类设计、物理公式计算 冒泡排序、异常处理、鲁棒性设计

1.3难度变化分析:

第一次作业是最基础的入门练习,主要目标是熟悉类的定义、封装以及数组操作。排序算法虽然简单,但手写选择排序加深了对算法的理解。

第二次作业难度明显提升,引入了货舱的概念,需要理解组合(货舱包含位置)和聚合(货舱装载货物)的区别。同时增加了输入校验框架,代码量翻倍。

第三次作业是功能最复杂的一次,新增了旅客管理(Passenger和Luggage的组合关系)和重心计算(物理公式转换为代码)。输入校验也更加完善,程序鲁棒性大幅提升。

二、设计与分析

2.1 第一次作业

2.1.1 类图设计

微信图片_20260517103139_387_2

图1 第一次作业类图

类职责分析:
类名 属性 方法 职责描述 SRP符合度
Item Id,kg getId(),getKg() 货物数据存储 符合
Plane code,limit, getLimit() 航班数据存储 符合
SorterTool 无 executeSort() 货物排序(选择排序) 符合
LoadPlan Plane,items,total startLoad(), isOverLimit(), display()
装载计划管理
部分符合
Main 无 Main() 输入输出、程序入口 符合

设计亮点:各类职责划分清晰,SorterTool独立出来负责排序,体现了“将变化隔离”的思想。
存在问题:LoadPlan同时承担了排序调用、重量累加和输出显示三个职责,虽然代码量不大,但不符合单一职责的严格要求。

2.1.2 复杂度分析

微信图片_20260515092943_383_2

图2 第一次作业代码复杂度报表
方法 圈复杂度(v(G)) 代码行数 分析
LoadPlan.display() 4 约8行 包含循环和分支判断,复杂度合理
SorterTool.executeSort() 4 约12行 选择排序算法,双重循环带来复杂度
LoadPlan.startLoad() 3 约8行 调用排序和累加,逻辑清晰
Main.main() 2 约15行 线性流程,复杂度低

心得:第一次作业的复杂度指标表现良好,最大圈复杂度仅4,说明代码逻辑简单清晰。平均方法语句数不到4行,方法粒度合理,便于阅读和调试。这得益于一开始就按照题目要求拆分了多个类。

2.2 第二次作业

2.2.1 类图设计

微信图片_20260517103350_388_2

图3 第二次作业类图

类职责分析:
类名 职责描述 关系类型 SRP符合度
Position 货舱位置(行、列) 被CargoCompartment组合 符合
Cargo 货物数据 被CargoCompartment聚合 符合
CargoCompartment 货舱管理 组合Position,聚合Cargo
符合
Flight 航班管理 聚合CargoCompartment 符合
LoadDispatcher 排序和装载调度 依赖Flight和CargoCompartment 符合
InputValidator 输入校验
静态工具类
符合
CargoWithTarget 货物+目标货舱包装 辅助类 符合

设计亮点:

  1. 严格遵循了题目提供的类图框架
  2. 货舱与位置是组合关系(货舱创建时自动生成位置列表)
  3. 货舱与货物是聚合关系(货物可独立存在)
  4. InputValidator作为静态工具类,体现了“高内聚低耦合”

存在问题:Position类虽然完整实现,但在最终输出中从未使用,是一个“过度设计”的案例。

2.2.2 复杂度分析

微信图片_20260515093754_384_2

图4 第二次作业代码复杂度报表
方法 圈复杂度(v(G)) 分析
LoadDispatcher.dispatch() 约8-10 包含遍历、条件判断、输出等多种逻辑,是复杂度最高的方法
CargoCompartment.getCurrentWeight() 约3 遍历累加,复杂度合理
Flight.findCompartment() 约3 遍历查找,复杂度合理
sortCargos() 约4 插入排序算法,双重循环
printOverallStatus() 约6 多个分支判断(超载情况组合)

心得:第二次作业的代码量比第一次翻倍,但通过合理的类划分,保持了较好的可读性。LoadDispatcher的dispatch方法圈复杂度相对较高,因为它包含了遍历、条件判断和输出多种逻辑,后续可以进一步拆分。

2.3 第三次作业

2.3.1 类图设计

微信图片_20260517103440_389_2

图5 第三次作业类图

类职责分析:
类名 职责描述 关系类型 SRP符合度
Passenger 旅客管理 组合Luggage 符合
Luggage 行李重量 被Passenger组合 符合
CargoItem 货物(带编号) 被CargoCompartment聚合 符合
CargoCoumpartment 货舱管理 聚合CargoItem 符合
Flight 航班管理 聚合Passenger、CargoCompartment 符合
BalanceCalculator 重心计算 依赖Flight(工具类) 符合
DataChecker 输入校验 静态工具类 符合

设计亮点:

  1. Passenger和Luggage的组合关系设计正确,Luggage在Passenger构造器内部创建
  2. BalanceCalculator作为纯计算工具类,所有常量定义为static final
  3. DataChecker统一处理输入异常,遇到非法输入立即退出

存在问题:CargoCompartment的getArm()方法通过id硬编码判断力臂值(id==1返回12.0,否则22.0),如果将来增加新货舱,必须修改该类。

2.3.2 复杂度分析

微信图片_20260517101436_386_2

图6 第三次作业代码复杂度报表
方法 圈复杂度(v(G)) 分析
BalanceCalculator.printReport() 约10-12 复杂度最高,承担计算+输出双重职责
sortById() 约4 冒泡排序,复杂度合理
DataChecker.readInt() 约3 包含存在性检查和负数检查
CargoCompartment.addItem() 约2 简单条件判断
Main.main() 约4 流程控制,复杂度低

心得:第三次作业虽然业务逻辑最复杂,但通过合理的工具类拆分(BalanceCalculator、DataChecker),保持了较低的圈复杂度。printReport方法代码行数较多(约50行),因为它同时承担了计算和输出两个职责,这是后续可以优化的方向。
2.4 三次作业复杂度对比
指标 第一次 第二次 第三次 趋势分析
类数量 5 8 8 职责越来越细分
最大圈复杂度 4 8-10 10-12 随业务复杂度上升
平均方法语句数 3.91 5-6 6-7 方法粒度变粗
总语句数 67 ~150 ~200 代码量翻倍

三、采坑心得

3.1 第一次作业的坑:排序时名称和重量不同步

问题描述:我第一次实现排序时,只交换了重量数组,忘记同步交换名称数组,导致输出时货物名称和重量对应错误。

错误输入:
水果 500
电子产品 850
体育用品 128

错误输出:
货物[水果 重量:850.0kg] ← 水果的重量应该是500,却显示了850

原因分析:当时把名称和重量分开存在两个数组里,排序时只交换了重量数组。

解决方案:将名称和重量封装到Item类中,排序时交换Item对象。

// 错误做法
String[] names = ...;
double[] weights = ...;
// 只交换weights,没交换names

// 正确做法
Item[] items = ...;
// 交换Item对象,名称和重量一起移动

教训:相关的数据应该封装在一起,不要分散存储。

3.2 第二次作业的坑:Position类定义了但没用
问题描述:我按照题目要求实现了Position类,并在CargoCompartment中创建了位置列表,但最终输出时完全没有用到这些位置信息。
反思:这是典型的“为了满足要求而写代码”,没有思考位置信息的实际用途。货舱的位置网格(如2行5列)应该用于记录每个位置装了哪件货物,但我只实现了存储,没有实现查询和展示。
改进方向:

  1. 在Cargo类中增加一个position字段,记录货物放在哪个位置
  2. 输出时显示每个位置的占用情况
  3. 增加位置分配算法(如从第一行第一列开始依次装载)

3.3 第三次作业的坑:力臂值硬编码
问题描述:在CargoCompartment类中,我通过货舱id判断力臂值:

public double getArm() {
    return id == 1 ? 12.0 : 22.0;
}

问题分析:这段代码有两个隐患:

  1. 如果将来增加中舱(id=3),力臂值是多少?代码无法处理
  2. 力臂值是货舱的固有属性,不应该通过id间接计算
    解决方案:在构造CargoCompartment时直接传入力臂值。
// 改进后的代码
class CargoCompartment {
    private int id;
    private double arm;  // 新增字段
    
    public CargoCompartment(int id, int rows, int cols, double maxLoad, double arm) {
        this.id = id;
        this.arm = arm;  // 直接赋值
        // ...
    }
    
    public double getArm() {
        return arm;  // 直接返回
    }
}
教训:对象的属性应该在创建时确定,而不是通过计算获得。

3.4 第三次作业的坑:输入校验的演进
问题描述:前两次作业的输入校验不完善,当用户输入负数或非数字时,程序会直接崩溃。
演进过程:
作业 校验方式 问题
第一次 无校验 非法输入直接崩溃
第二次 有校验框架但未充分使用 部分地方仍会崩溃
第三次 统一的DataChecker类 所有输入都经过检验

最终方案(DataChecker类):

public static int readInt(Scanner sc) {
    // 检查是否有整数输入
    if (!sc.hasNextInt()) {
        System.out.println("输入格式错误!");
        System.exit(0);
    }
    int v = sc.nextInt();
    // 检查是否为负数
    if (v < 0) {
        System.out.println("数值不能为负数!");
        System.exit(0);
    }
    return v;
}
教训:输入校验应该在所有作业中统一处理,而不是到最后才完善。

四、改进建议
4.1 第一次作业的改进
问题 当前实现 改进建议
Item类名 Item 改为Cargo,更符合业务语义
SorterTool实例化 每次new SorterTool() 改为静态方法,直接类名调用
LoadPlan职责过重 承担排序、计算、输出 拆分为LoadScheduler和LoadReporter

改进代码示例:

// 改进前
SorterTool tool = new SorterTool();
tool.executeSort(source);

// 改进后
SorterTool.executeSort(source);  // 静态方法

4.2 第二次作业的改进
问题 改进建议
Position类未使用 在输出中显示每个位置的占用情况
sortCargos方法名 改为sortCargosByWeightDesc,更明确
CargoWithTarget类冗余 直接在Cargo类中增加targetld属性

4.3 第三次作业的改进

问题 改进建议
力臂硬编码 构造时传入力臂值
printReport方法过长 拆分为calculate()和printReport()
乘客姓名缺失 增加姓名字段,从输入读取

改进代码示例(printReport拆分):

// 改进前:一个方法做所有事
public void printReport(Flight flight) {
    // 计算逻辑...
    // 输出逻辑...
}

// 改进后:职责分离
public BalanceData calculate(Flight flight) {
    // 只做计算,返回结果对象
    return new BalanceData(totalW, totalM, cg, cgPercent);
}

public void printReport(BalanceData data) {
    // 只做输出
}

五、总结
5.1 学到了什么
技术层面:

  1. 面向对象设计原则:单一职责原则(SRP)在实际编码中的应用。三次作业的类数量从5个增加到8个,职责越来越清晰
  2. 类关系的理解:组合(货舱包含位置)和聚合(货舱装载货物)的本质区别
  3. 排序算法:手写选择排序、插入排序、冒泡排序,加深了对时间复杂度的理解
  4. 输入校验:统一的DataChecker类提高了程序鲁棒性
  5. 物理公式转代码:重心计算公式的正确实现,浮点数精度处理
    工程层面:
  6. 代码从“能跑就行”到“可读可维护”的转变
  7. 迭代开发中,前一次的设计会影响后一次的扩展
  8. 测试的重要性:第三次作业花了大量时间做边界测试
    5.2 需要继续学习的方向
  9. 设计模式:策略模式可以优化排序算法的切换,工厂模式可以优化对象的创建
  10. 单元测试:JUnit的使用,在代码编写前先写测试用例
  11. 代码重构:如何识别代码坏味道(如过长方法、硬编码),并系统地重构
  12. UML建模:更专业的类图、时序图画法
    5.3 对课程的建议
  13. 理论课:建议在讲设计原则时,结合作业中的具体案例,比如用第三次作业讲解“开闭原则”
  14. 实验课:可以安排一次专门讲SourceMonitor、PlantUML等工具的使用
  15. 研讨课:建议增加代码走查环节,选几位同学的代码现场分析优缺点
  16. 作业迭代:目前的迭代设计很好(难度递进),建议保持这个节奏
posted @ 2026-05-18 18:49  李意琪  阅读(10)  评论(0)    收藏  举报