航空器配载系统三次作业总结

一、设计与分析
第一次作业:基础货运配载
第一次作业要求很简单:输入航班号、最大载重、货物件数,然后输入每件货物的名称和重量。系统需要按重量从高到低排序后输出货物信息,计算总重量,判断是否超载。

我的类设计分了三个:Cargo类存货物名称和重量,提供getter;Flight类存航班号、最大载重和当前总重量,提供添加货物和超载判断的方法;Main类负责读取输入、排序、调用Flight并输出。

类关系很简单,Main依赖Cargo和Flight,Flight聚合Cargo(实际上Flight没有存货物列表,只累加了总重量)。这个设计基本符合单一职责,Cargo只管货物数据,Flight只管航班信息,Main只管流程控制。

用SourceMonitor分析了一下,Main的圈复杂度大概3(主要来自排序的双层循环),Flight复杂度2,Cargo复杂度1。整体代码很干净。

不过有一个问题:排序逻辑直接写在了Main里,如果以后要改排序规则(比如按重量升序或者按名称排序),就得改Main,不太灵活。更好的做法是抽一个Sorter类,但第一次作业这样写也说得过去。

第二次作业:多货舱管理与重量排序装载
第二次作业加了一堆要求:货舱要有独立的最大载重和位置网格(行、列),货物要指定目标货舱,先按重量降序排序再依次装载,每个货舱单独判断超载,航班还要有最大起飞重量和最大业载重量两个指标。

题目明确要求必须遵循单一职责原则,还给出了参考类图。我按照要求实现了以下类:

Position:表示货舱中的一个位置,存行号和列号,提供位置名称。这次作业其实没怎么用到具体位置,只是创建了列表,可能是为以后扩展预留的。

Cargo:增加了目标货舱ID属性。

CargoCompartment:货舱类,包含ID、最大载重、位置列表(组合关系,构造时生成)、已装货物列表(聚合关系)。提供添加货物(检查容量)、获取当前重量、判断是否超载的方法。

Flight:包含航班号、最大起飞重量、最大业载重量、货舱列表。提供根据ID查找货舱、计算总货物重量的方法。

LoadDispatcher:调度类,负责货物按重量降序排序(用Collections.sort加匿名Comparator),以及查找货物。

InputValidator:输入校验类,提供整数和浮点数的范围校验方法。

类图关系:Flight聚合CargoCompartment,CargoCompartment聚合Cargo并组合Position。LoadDispatcher依赖Flight和货物列表。InputValidator是工具类。

复杂度方面,排序用了Collections.sort(TimSort,O(n log n)),比较器直接比较double值。题目要求“同等重量的货物按输入顺序”,Collections.sort是稳定排序,所以自然满足。getCurrentWeight方法遍历货物列表累加,O(n)。整体圈复杂度不高。

浮点数比较时我加了个小误差容忍,用“当前重量+货物重量 ≤ 最大载重+1e-9”来判断,避免因精度问题导致刚好等于时误判为超载。这算是一个小经验。

第三次作业:配平计算
第三次作业在第二次基础上增加了旅客(带行李)、重心计算、MAC百分比、输入合法性严格校验,还特别要求不能使用继承和多态,排序必须用冒泡排序且不能用Collections.sort和lambda。新增了Passenger、Luggage、WeightBalanceCalculator,优化了InputValidator。

我的实现:

Passenger:包含Luggage组合(构造器内new Luggage),计算总重量时用标准体重75kg加上行李重量。没有暴露修改行李的方法,符合要求。

Luggage:只记录行李重量。

WeightBalanceCalculator:纯静态工具类,提供generateLoadSheet方法。内部定义了常量:空机重量40000kg、空机力臂16.25m、旅客舱力臂18m、前货舱力臂12m、后货舱力臂22m、MAC长度5m、MAC前缘距基准15m、安全范围25%到38%。计算流程:遍历旅客累加重量和力矩,遍历货舱累加货物重量和力矩(根据货舱ID区分力臂),然后算全机总重和总力矩,得出实际重心,再换算成MAC百分比,最后输出舱单和评估结果。

LoadDispatcher:改为冒泡排序,同时排序货物列表和目标舱位ID列表,保证对应关系。冒泡排序是稳定的,重量相等时不交换,保持了输入顺序。

InputValidator:增加getNonNegativeInt和getNonNegativeDouble,遇到负数或非数字立即输出错误并退出程序。

Flight:增加旅客列表属性和添加旅客的方法。

Main:按题目要求的顺序读取输入(前舱行列号及最大载重、后舱行列号及最大载重、旅客人数及每人行李重量、货物总件数、前舱货物件数、然后分别读前舱和后舱货物)。用校验类读入,创建货舱,装载货物时如果超载立刻输出警告并退出。最后调用WeightBalanceCalculator生成舱单。

类图变化:Flight关联Passenger(1对多),Passenger组合Luggage。WeightBalanceCalculator依赖Flight和LoadDispatcher。

二、采坑心得
做这三次作业踩了不少坑,记录几个印象最深的:

坑1:Scanner的nextLine混用问题

第一次作业用nextLine读航班号,然后nextDouble读最大重量,再nextInt读货物件数。结果后面循环里用nextLine读货物名称时读到了空串。查了半天发现是nextInt后留下了换行符。我后来统一用了next()读字符串,因为货物名称没有空格,避开了这个问题。

坑2:浮点数比较超载边界

第一次判断超载直接用“总重量 > 最大载重”。但有个测试用例总重量刚好等于最大载重,按理说不超载,可浮点数运算后可能变成2000.0000001,导致误判。后来统一用了“总重量 > 最大载重 + 1e-9”,留个误差容忍。

坑3:Collections.sort的稳定性理解

第二次作业要求同等重量货物按输入顺序输出,我本来担心sort会打乱顺序,查了文档发现List.sort是稳定排序,只要compare返回0就不交换相对位置。测试通过了。

坑4:忘记同步交换两个列表

第三次作业写冒泡排序时,一开始只交换了货物列表,忘记同步交换目标舱位ID列表,导致排序后货物和目标舱位对不上。调试了很久才发现,后来在交换货物前后加上交换ID列表,问题解决。

坑5:力臂映射写错

第三次作业要求前货舱力臂12m、后货舱22m,我一开始把前后写反了,导致计算结果和样例对不上。检查后发现是判断货舱ID时写错了条件。

坑6:输入合法性校验的顺序

第三次作业要求读到非法数值立即停止。校验顺序很重要:先读前舱参数,如果前舱行数是负数,直接退出,不会继续读后面的输入。另外前舱货物件数必须在0到总件数之间,这个校验放在Main里处理,因为涉及两个输入值的关联。

坑7:空机力臂保留一位小数

输出样例中空机力臂是16.3m,常量定义是16.25,printf("%.1f")会自动四舍五入。这点一开始没注意,以为要手动处理,后来发现格式化就能搞定。

三、改进建议
虽然作业都完成了,但回头看有些地方可以做得更好:

  1. 第一次作业增加输入校验

第一次没有强制输入校验,但如果用户输入负数重量,程序会崩溃。可以增加简单校验,比如重量大于0。

  1. 第二次作业的Position类未实际使用

Position类创建了大量对象,但装载货物时完全没有用到,只是为了满足类图。改进的话可以在添加货物时分配具体位置,输出时显示货物放在哪一行哪一列。

  1. 第三次作业的System.exit

遇到非法输入直接调用System.exit结束程序,实际系统中不可取,应该抛出异常或循环重新输入。但作业要求“立即停止”,所以勉强接受。

  1. 排序算法的选择

第三次作业强制冒泡排序,货物件数≤100没问题。但实际开发中应该用更高效的排序。不过作为学习目的,手写冒泡有助于理解原理。

  1. 硬编码常量

WeightBalanceCalculator中所有力臂、空机重量都是硬编码。如果机型变化,就要改源码。改进方式是将参数放在配置文件中或通过构造器传入。

  1. 重复的输出格式代码

三次作业都有输出格式化代码,特别是第三次输出舱单用了很多printf。可以抽一个Formatter类专门负责输出。

  1. 货物排序与装载分离

第二次作业的LoadDispatcher负责排序和装载,第三次装载放在了Main里。可以统一成一个CargoLoader类,接收货物列表和货舱映射,内部排序后尝试装载并返回结果。

四、总结
这三次作业让我体会到了迭代开发的感觉。第一次写简单类,第二次加功能时发现需要重新组织结构,第三次引入新概念但尽量不破坏原有代码。虽然被各种bug折磨,但最终跑通时很有成就感。

学到的东西:

单一职责原则:每个类只做一件事,改一个地方不影响其他。

集合框架的使用:ArrayList的遍历、排序、交换元素,冒泡排序加深了对稳定性的理解。

浮点数精度处理:商业计算中不用==直接比较,要留误差范围。

物理计算在软件中的落地:力矩、重心、百分比公式。

输入校验的重要性:用户可能输入任何垃圾数据,程序必须能处理。

需要进一步学习的地方:

异常处理:我用了很多System.exit,应该学习try-catch和自定义异常。

设计模式:比如策略模式用于不同排序算法,工厂模式用于创建货舱。

单元测试:每次改完手动输入测试数据效率低,学习JUnit自动化测试。

代码重构:第三次作业代码已经比较臃肿,可以抽取公共方法。

对课程的建议:

作业迭代设计得好,循序渐进。希望以后提供更多测试用例方便自测。

课上可以多讲实际工程中的代码组织方式。

实验课可以安排代码走查,互相看设计取长补短。

总之,这三次作业写得很累但收获满满。现在看到飞机的载重平衡报告都能看懂了,也算跨界知识。以后写代码会更注意类设计和边界条件处理。

posted @ 2026-05-18 22:51  liu12138  阅读(10)  评论(0)    收藏  举报