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

一、前言

1.1 作业知识点概览

三次作业围绕“航空器配载与货运管理”这一真实业务场景,层层递进地考察了面向对象设计的核心概念。

1.1.1作业1知识点:封装、对象数组初始化、选择排序(降序)、单一职责原则、格式化输出、超载判断逻辑。

1.1.2作业2知识点:组合关系、聚合关系、List集合框架、数组动态初始化、增强for循环查找、多重条件超载状态码返回、Scanner分割符解析、静态工具方法设计、封装。

1.1.3作业3知识点:组合关系、聚合关系、静态工具类方法设计、力矩平衡物理公式实现、四舍五入浮点运算、输入负数校验及提前return终止、CG%安全区间判断、配载评估输出。

1.2 题量与难度分析

1.2.1题量:三次作业均为单题,但代码规模逐次扩大(作业1约100行有5个类, 作业2约200行有7个类, 作业3约300行有10个类)
1.2.2难度:渐进式提升。
作业1为基础入门,理解单一职责,手写排序算法,较为基础简单。
作业2强调类间关系设计,区分组合与聚合,管理多货舱状态,在第一题基础上提升了一些难度。
作业3引入物理计算且故意禁用Lambda和Collections.sort(),需要严格遵守设计约束(无继承/无多态/静态工具类),考察基础算法的掌握程度,较难。

二、设计与分析

2.1 第一次作业源码分析

2.1.1报表

屏幕截图 2026-05-17 215514

报告核心数据解读:
121 总代码行数
91 有效语句数(不含空行)
类的数量为5
平均每个类有约2.8个方法
每个方法平均只有4.36条语句,方法颗粒度较细
注释率为0%,最复杂的方法是主方法

2.1.2类图:

微信图片_20260518175720_29_1

类图分析

共五个类:

Main类是程序入口,负责流程编排和输入输出,包含main方法。

Flight类负责航班信息存储,包含flightNo(航班号)、maxWeight(最大载重)、num(货物数量)三个私有属性,提供setFlight和相应的getter方法。

Cargo类负责货物信息存储,包含Name(货物名称)和Weight(货物重量)两个私有属性,提供setCargo和getter方法。

LoadManifest类负责装载计算和超载判断,包含cargo数组、flight对象、totalweight总重量、isoverload超载标志四个私有属性,提供setLoadManifest、totalWeight、isOverload及对应的getter方法。

CargoSorter类专门负责货物排序,包含cargoSorter方法,采用选择排序算法实现货物按重量降序排列。

各类之间关系清晰:Main依赖Flight、Cargo、LoadManifest和CargoSorter;LoadManifest聚合Cargo数组和Flight对象;CargoSorter依赖Cargo数组进行操作。这种设计初步体现了单一职责原则——每个类只做一件事,排序、装载、判断超载各司其职。

2.2 第二次作业源码分析

2.2.1报表
屏幕截图 2026-05-17 215520

报告核心数据解读:
231 总代码行数(较V1增长91%)
168 有效语句数
分支语句占比15.5
方法调用语句数69
注释率略有提升0.9
类的数量为7(较V1增加2个)
平均每个类4个方法
每个方法平均4.11条语句

与V1对比,V2的代码行数增长了91%,类数量增加了40%,方法数量从12个增加到约28个。注释率从0%提升到0.9%,说明开始关注代码可读性。分支语句占比15.5%略高于V1的13.2%,体现了多货舱判断逻辑的复杂性。

2.2.2类图:

微信图片_20260518175721_30_1

类图分析:

共7个类:

Flight类增加了cargoCompartment列表,用于存储多个货舱,提供addCargoCompartment、findCargoCompartment、getTotalWeight等方法。

CargoCompartment货舱类包含hold_id(货舱ID)、hold_maxloadweight(最大载重)、hold_currentweight(当前载重)、positions数组和cargos列表。关键设计是:positions数组在构造器中通过双重循环直接创建,体现了组合关系;cargos列表通过addCargo方法添加货物,体现了聚合关系。提供addCargo、getId、getCargos、getCurrentWeight、getHoldMaxLoadWeight等方法。

Position位置类包含hold_row和hold_col两个属性,提供getPosName、getRow、getCol方法。

Cargo类增加了targethold属性,表示目标货舱ID。

LoadDispatcher类提供静态方法sortCargos实现货物排序,以及flightsoverLoad方法返回航班超载状态码。

InputValidator类提供三个静态校验方法:isValidInt、isValidDouble、isNotEmpty。

各类之间关系:Flight与CargoCompartment是聚合关系(一对多),CargoCompartment与Position是组合关系,CargoCompartment与Cargo是聚合关系,LoadDispatcher依赖Cargo和Flight,InputValidator被Main依赖。

2.3 第三次源码分析

2.3.1报表
屏幕截图 2026-05-17 215614

报告核心数据解读:
301 总代码行数(较V2增长30%)
227 有效语句数
分支语句占比8.8(较V2下降)
方法调用语句数86
注释率约0.7%
类的数量为9(较V2增加2个)
平均每个类4.22个方法
每个方法平均4.21条语句
与V2对比,V3的代码行数增长了30%,类数量增加了2个,方法数量从28个增加到约38个

2.3.2类图

微信图片_20260518175722_31_1

类图分析:

新增Passenger(旅客类)、Luggage(行李类)和WeightBalanceCalculator(重量平衡计算器),系统总类数达到9个:

Flight类增加了passengers列表,用于存储旅客,提供addPassenger和getPassenger方法。

Passenger旅客类包含name(姓名)和luggage(行李对象),在构造器中接收行李重量并内部new Luggage对象,体现组合关系。提供getPassengerName和getTotalWeight方法(总重=75kg标准体重+行李重量)。

Luggage行李类包含luggage_weight属性,在构造器中初始化,提供getLuggageWeight方法。

CargoCompartment类增加了positions数组的正确实现,在构造器中通过双重循环创建Position对象数组。

WeightBalanceCalculator类是本次作业的核心,提供多个静态方法:generateLoadSheet生成配载单,cgisover判断重心是否安全,getPassengerWeight计算旅客总重,getTotalWeight计算全机总重,getTotalMoment计算总力矩,getActualCG计算实际重心,getCGPercent计算重心百分比。该类不持有Flight成员变量,所有方法均通过参数传入Flight对象,严格遵守依赖关系设计约束。

三、踩坑心得

3.1 作业1踩坑

第一个坑是Scanner的换行符问题。在nextDouble()后直接使用nextLine()会读到空字符串,必须在中间额外调用一次nextLine()吸收换行符。

第二个坑是对象数组初始化。声明Cargo[]数组后,每个元素必须单独new实例化,否则会出现空指针异常。

第三个坑是超载判断时机。最初先添加货物再判断是否超载,导致超载数据被错误录入。正确做法是先判断后添加。

3.2 作业2踩坑:

第一个坑是对组合关系的误解。最初我为CargoCompartment提供了addPosition()方法让外部添加位置,这破坏了组合关系的唯一性。正确做法是位置必须在构造器中一次性创建完毕,不对外暴露添加位置的公共方法。

第二个坑是货舱查找逻辑。通过ID遍历匹配时,需要注意ID的数据类型匹配和空值处理。

第三个坑是双重超载判断的状态码设计。需要区分三种超载情况:仅超起飞重量、仅超业载重量、两者都超,以及正常状态共四种状态码。

3.3 作业3踩坑

第一个坑是力臂映射错误。最初将前舱和后舱的力臂值写反了(前舱22.0m,后舱12.0m),导致重心计算结果偏离安全范围。修正后建立舱室ID到力臂的明确映射关系。

第二个坑是WeightBalanceCalculator的设计约束。被要求不能持有Flight成员变量,必须通过方法参数传入,这考验了对依赖关系与关联关系的理解。

第三个坑是旅客行李的组合关系实现。Luggage必须在Passenger构造器内部实例化,不对外暴露setter方法,体现了良好的封装性。

第四个坑是输入校验。需要在数据录入过程中对数据进行合法性验证,发现非法输入立即停止应用程序运行。

四、改进建议

针对当前代码的改进方向

第一,力臂硬编码问题。当前力臂值写在计算类中,建议将力臂作为CargoCompartment的属性,支持配置化,可引入配置管理器从配置文件读取力臂参数,提高灵活性。

第二,货舱类型固定。当前仅支持前舱和后舱两个货舱,可设计为可扩展的货舱列表,支持任意数量货舱。

第三,缺乏数据持久化。程序关闭后数据丢失,可增加文件存储或数据库接口,支持航班配载方案的保存和加载。

第四,排序算法单一。当前仅使用冒泡排序,可封装排序策略,支持多种排序算法切换(如选择排序、插入排序等)。

第五,注释率偏低。三次作业的注释率均低于1%,建议为每个类和方法添加必要的职责说明和参数注释,提高代码可读性和可维护性。

可持续改进路径

第一阶段实现配置化(力臂可配、货舱数量可配),第二阶段实现持久化(数据库存储),第三阶段增加图形化界面,第四阶段引入智能推荐装载方案。

五、总结

5.1 学习成果

通过这三次作业,我从理论到实践系统地掌握了面向对象程序设计的核心内容,具体体现在以下几个方面:

设计原则方面,我深刻理解了单一职责原则(SRP)的落地实践。在V1中,我将排序、装载、超载判断分别交给CargoSorter、LoadManifest、Flight三个类处理,避免了将所有逻辑堆积在Main类中的“上帝类”反模式。在V2中,CargoCompartment只负责货舱的载重管理,Position只负责位置信息存储,各司其职。在V3中,WeightBalanceCalculator专门负责力矩计算,InputValidator专门负责输入校验。这种设计使得每个类都有明确的职责边界,修改某一功能时只需关注对应的类,降低了代码的耦合度。

类间关系方面,我掌握了依赖、关联、聚合、组合四种关系的区别与实现方式。依赖关系是最弱的关系,如LoadDispatcher依赖Cargo数组进行操作;关联关系表示类之间的长期联系,如Flight关联多个CargoCompartment;聚合关系是“整体-部分”的弱拥有关系,部分可以独立存在,如CargoCompartment聚合Cargo,货物被移除后仍可独立存在;组合关系是“整体-部分”的强拥有关系,部分的生命周期由整体控制,如CargoCompartment组合Position,货舱销毁时其位置网格也随之销毁。理解这些关系对于设计高内聚低耦合的系统至关重要。

算法基础方面,我手写了选择排序和冒泡排序算法。选择排序的核心思想是每次从未排序序列中选出最大(或最小)元素,放到已排序序列的末尾;冒泡排序的核心思想是重复遍历序列,每次比较相邻元素并交换逆序对。两种排序算法的时间复杂度均为O(n²),但选择排序的交换次数更少(最多n-1次),而冒泡排序在序列基本有序时效率更高。通过手写排序算法,我加深了对算法稳定性的理解——冒泡排序是稳定的,选择排序是不稳定的。

物理建模方面,我掌握了航空器载重平衡计算的完整流程。力矩平衡公式为:总力矩除以总重量等于实际重心。计算步骤包括:计算旅客总重量及力矩(单名旅客总重=75kg标准体重+行李重量),计算各货舱总重量及力矩(重量乘以对应力臂),计算全机总重量与总力矩(空机+旅客+货舱),计算实际重心,最后换算为重心百分比并评估安全状态。这让我体会到物理公式与编程实现的结合方法,特别是浮点数精度处理——使用double类型并保留一位小数输出。

鲁棒性设计方面,我学会了输入校验的完整设计。包括负数校验(货物重量不能为负数)、范围校验(行数列数不能为负数)、空值校验(字符串不能为空)。当发现非法输入时,立即输出错误提示并使用return提前终止程序,避免后续逻辑基于错误数据执行。

度量分析方面,我学会了使用SourceMonitor分析代码复杂度。圈复杂度(v(G))衡量方法的逻辑分支数量,健康值应≤10;嵌套深度衡量代码的可读性,健康值应≤3;注释率衡量代码的文档完整性,企业级开发通常要求≥20%。通过度量报告,我可以量化评估代码质量并找到需要重构的代码段。

5.2 待深入学习方向

设计模式方面,本次作业中的LoadDispatcher可优化为策略模式。当前排序算法固定在代码中,若需要切换为不同的装载策略(如按重量排序装载、按体积排序装载、按优先级装载),则需要修改LoadDispatcher类的代码,违反了开闭原则。引入策略模式后,可以定义装载策略接口,不同的装载算法实现该接口,通过依赖注入选择具体策略,实现算法的灵活切换。

单元测试方面,可引入JUnit框架对每个类进行独立测试。例如测试CargoCompartment的addCargo方法:正常添加应返回true且当前重量增加,超载添加应返回false且当前重量不变。编写单元测试可以及早发现代码中的逻辑错误,并在后续重构时确保原有功能不被破坏。

代码重构方面,需要培养识别代码坏味道的能力。常见的坏味道包括:重复代码(同一段代码出现在多个地方)、过长方法(方法超过50行)、过大的类(类超过200行)、发散式变化(一个类因多个不同原因被修改)等。掌握相应的重构手法(如提取方法、提取类、移动方法等),可以持续改进代码结构。

5.3 对课程的建议

作业的渐进式迭代设计非常好,从单货舱到多货舱再到力矩计算,逻辑清晰、层层递进,每次迭代都在保留原有功能的基础上增加新特性,非常接近真实项目的开发模式。

建议后续增加代码评审环节,由教师展示优秀作业和典型问题,促进同学之间的交流学习,帮助大家共同进步。实验课可以增加一次重构实验,让学生对自己的V1代码进行重构升级到V3,体会迭代开发的优势和重构的重要性。此外,可以提前提供SourceMonitor等工具的使用教程,减少环境配置耗时,让学生将更多精力放在代码设计和算法实现上。

5.4 结束语

三次作业从简单的货物超载判断,到多货舱管理,再到完整的载重平衡计算,让我完整经历了一个软件系统的迭代开发过程。每次迭代都在保留原有功能的基础上增加新特性,同时不断优化设计结构。这种开发模式非常接近真实项目开发,让我受益匪浅。

虽然V3的设计约束很严格(禁用Lambda、禁用sort、禁止继承多态),但也正是因为这些约束,才让我真正理解了面向对象设计的内涵——封装不只是private关键字,而是隐藏实现细节;继承不只是extends关键字,而是表达“is-a”关系;多态不只是override,而是面向接口编程。当这些语法糖被禁用时,我必须回归问题的本质:如何用最基础的方式组织代码、划分职责、建立关系。

通过SourceMonitor的量化分析,我也学会了用数据评估代码质量,理解了圈复杂度、嵌套深度、注释率等指标对代码可维护性的影响。这将对我今后的编程实践产生深远影响。

最后,感谢老师设计了这样一套环环相扣、逐步深入的作业体系,让我在实践中掌握了面向对象设计的核心概念,为后续的软件开发学习打下了坚实的基础。

posted @ 2026-05-18 19:43  lyhs  阅读(2)  评论(0)    收藏  举报