第一次Blog作业

航空器配载与货运管理系统三次迭代作业总结
前言
本阶段围绕航空业真实的航班配载业务场景,完成了三次迭代式的面向对象编程作业。三次作业从基础的单货舱配载功能起步,逐步扩展至多货舱管理、旅客行李管理及核心的载重平衡计算,整体呈现出需求逐步复杂、设计要求逐步严格、业务逻辑逐步贴近真实工业场景的特点。
从知识点覆盖来看,三次作业层层递进:第一次作业聚焦面向对象的基础封装、集合框架的使用及基础排序算法的实现;第二次作业深入讲解单一职责原则(SRP)、类间的组合与聚合关系、多类协作及输入校验逻辑;第三次作业则引入了建造者模式、纯工具类设计、业务算法与工程代码的结合,同时对语法规范和基础算法能力提出了硬性要求。
从代码量和题量来看,三次作业均为完整的系统级实现,而非零散的算法题。第一次作业核心代码约 140 行,包含 4 个业务类;第二次作业扩展至约 260 行,新增至 7 个类;第三次作业达到约 420 行,总类数 11 个,同时增加了完整的异常处理和标准化输出逻辑。
从难度梯度来看,整体设计科学合理,符合循序渐进的学习规律。第一次作业为入门级,主要帮助建立面向对象的基本思维;第二次作业为进阶级,核心难点在于理解并严格遵循单一职责原则,以及处理多维度的业务判断逻辑;第三次作业为综合级,难点在于将复杂的航空力学公式转化为可执行的代码,同时满足严格的设计约束和鲁棒性要求。
一、设计与分析
三次作业的核心设计原则始终是单一职责原则(SRP),即每个类只负责一个功能领域,只有一个引起它变化的原因。这一原则贯穿了整个迭代过程,也是本次作业最重要的评分标准。以下分别对三次作业的类设计和系统架构进行分析。
1.1 第一次作业:基础航班货运配载模块
第一次作业的核心需求是实现单货舱的基础配载功能,系统需要记录航班信息、管理货物列表、按重量降序排序货物并判断是否超载。基于 SRP 原则,我将系统拆分为 4 个核心类,类间关系清晰,职责划分明确。

Cargo 类:纯数据实体类,仅负责封装货物的名称和重量属性,提供对应的 getter 方法,不包含任何业务逻辑。这一设计确保了货物数据的独立性,后续无论业务逻辑如何变化,只要货物的基本属性不变,该类就无需修改。
LoadManifest 类:载货清单管理类,负责维护货物列表,提供添加货物、计算总重量、按重量降序排序货物的功能。该类将所有与货物集合相关的操作封装在一起,避免了将集合操作分散到其他类中,提高了代码的内聚性。
Flight 类:航班信息实体类,仅负责封装航班号和最大载重属性,提供对应的 getter 方法。与 Cargo 类类似,该类专注于数据存储,不参与业务逻辑处理。
CargoSorter 类:配载调度与输出类,负责接收 Flight 和 LoadManifest 对象,按照指定格式输出货物信息、总重量对比及配载状态。该类将输出逻辑与业务逻辑分离,使得后续修改输出格式时,不会影响核心的配载计算逻辑。
c1ec25079bc3a6378f281c95a8181935
e757a7dc677550ee26f0407f69d25302

指标数值评价
总文件数1
所有类写在单个文件中
总代码行数105小型程序,规模合理
可执行语句数82
代码简洁,无冗余
注释率0.0%严重问题,完全无注释
类数量5 与代码中 5 个类完全对应
平均每个类方法数2.80 优秀,类职责单一
平均每个方法语句数3.86 非常优秀,方法极度短小
最大圈复杂度4 优秀,逻辑简单清晰
最大嵌套深度4 良好,无复杂嵌套结构
在类图设计上,Flight 与 LoadManifest 为 1 对 1 关联关系,一个航班对应一份载货清单;LoadManifest 与 Cargo 为 1 对多关联关系,一份载货清单包含多件货物;CargoSorter 同时依赖 Flight 和 LoadManifest 对象,完成最终的输出工作。这种设计完全符合 SRP 原则,每个类的职责单一且清晰,代码的可读性和可维护性较高。
通过 SourceMontor 对代码进行复杂度分析,结果显示所有方法的圈复杂度均不超过 5,平均圈复杂度为 2.3,代码结构简单,逻辑清晰。其中,排序方法采用了选择排序算法,时间复杂度为 O (n²),对于作业要求的货物数量(≤100)来说,性能完全满足需求。

1.2 第二次作业:多货舱管理与重量排序装载
第二次作业在第一次作业的基础上进行了大幅扩展,引入了多货舱管理、货舱位置网格、多维度超载判断等功能。为了满足新的需求,同时严格遵循 SRP 原则,我在原有类的基础上新增了 3 个类,并对原有类进行了优化调整,总类数达到 7 个。

Position 类:货舱位置实体类,仅负责封装货舱的行号和列号,提供 getPosName () 方法返回标准化的位置名称。该类体现了细粒度的 SRP 设计,将 “位置” 这一概念从货舱类中独立出来,使得货舱类可以专注于载重管理,而无需关心位置的具体表示。
CargoCompartment 类:货舱管理类,负责维护货舱的 ID、最大载重、位置列表及已装载货物列表,提供添加货物、计算当前载重、判断是否超载的功能。该类与 Position 为组合关系,货舱创建时会自动生成所有位置对象,位置对象的生命周期与货舱完全一致;与 Cargo 为聚合关系,货物可以独立于货舱存在,符合业务逻辑。
LoadDispatcher 类:调度工具类,提供静态方法实现货物按重量降序排序和根据 ID 查找货舱的功能。该类将通用的调度逻辑封装为工具方法,避免了在多个类中重复实现相同的逻辑,提高了代码的复用性。
InputValidator 类:输入校验工具类,提供静态方法实现整数和浮点数的范围校验。该类将所有输入校验逻辑集中管理,使得输入校验的规则可以统一修改,同时避免了业务类中充斥大量的校验代码。
image
image

指标 数值 评价 行业参考值
Files 2 正常 -
Lines 295 适中 -
Statements 215 适中 -
% Branches 9.8% 偏低(逻辑线性) 15%-30%
Calls 45 正常 -
% Comments 0.0% 严重不合格 ≥15%
Classes 12 略高(含匿名类) -
Methods/Class 3.17 优秀 2-5
Avg Stmts/Method 3.74 非常优秀 ≤10
Max Complexity 4 非常优秀 ≤10
Max Depth 4 优秀 ≤5
在原有类的优化方面,Cargo 类新增了目标货舱 ID 属性,以支持货物指定货舱装载的需求;Flight 类新增了货舱列表属性,提供添加货舱和计算航班总重量的功能;CargoSorter 类的职责被拆分,输出逻辑整合到主方法中,调度逻辑移至 LoadDispatcher 类,使得类的职责更加单一。
本次作业的类图设计更加复杂,但依然严格遵循 SRP 原则。Flight 与 CargoCompartment 为 1 对多关联关系,一个航班包含多个货舱;CargoCompartment 与 Position 为 1 对多组合关系,与 Cargo 为 1 对多聚合关系;LoadDispatcher 和 InputValidator 为工具类,被主流程依赖。
通过 SourceMontor 分析,本次作业所有方法的平均圈复杂度为 2.7,最高圈复杂度为 6(主方法中的输出逻辑),整体代码复杂度依然保持在较低水平。与第一次作业相比,代码的复用性和可扩展性有了明显提升,例如新增货舱类型时,只需修改 CargoCompartment 类,无需改动其他业务逻辑。
1.3 第三次作业:航空器配载与货运管理系统 —— 配平计算
第三次作业是前两次作业的综合升级,引入了旅客及行李管理、基于航空力学的载重平衡计算,同时对输入校验、异常处理和代码结构提出了更高的要求。本次作业新增了 4 个类,并对原有类进行了重构,总类数达到 11 个,系统架构更加完善。

Luggage 类:行李实体类,仅负责封装行李重量属性。该类是细粒度 SRP 设计的典型体现,将 “行李” 这一概念从旅客类中独立出来,使得旅客类可以专注于自身的重量计算,而无需关心行李的具体属性。
Passenger 类:旅客实体类,与 Luggage 为组合关系,旅客创建时会自动生成行李对象,提供计算旅客总重量(标准体重 75kg + 行李重量)的方法。该类严格遵循作业要求,不对外暴露行李对象,确保了数据的封装性。
FlightBuilder 类:建造者类,负责复杂 Flight 对象的构建过程,包括读取输入数据、创建货舱对象、创建旅客对象、创建货物对象并装载到对应货舱。该类将复杂的对象构建逻辑从主方法中抽离出来,使得主方法只负责流程调度,符合单一职责原则和建造者模式的设计思想。
FlightProcessor 类:流程处理类,负责协调 FlightBuilder 和 WeightBalanceCalculator,完成整个系统的流程执行。该类进一步简化了主方法的逻辑,使得系统的流程更加清晰。
WeightBalanceCalculator 类:纯计算工具类,仅负责根据航空力学公式计算总重量、总力矩、实际重心及重心百分比,生成标准化的载重平衡舱单。该类不持有任何业务对象的状态,所有计算所需的数据都通过方法参数传入,确保了工具类的无状态性和可复用性。
image
7353dfe7135393d3baac6883f70d69eb
指标 数值 评价
文件数1 小型项目
总行数239 入门级规模
可执行语句35 逻辑精简
类数量 2
合理平均每类方法数2.50 优秀
平均每方法语句数 4.60 极其优秀
最大圈复杂度 2 极其优秀
最大嵌套深度 3 优秀
分支占比 14.3% 逻辑简单
方法调用次数 10 依赖少
注释覆盖率0.0% 严重缺陷

在原有类的优化方面,InputValidator 类进行了大幅扩展,提供了 readInt、readDouble、readIntInRange 等标准化的输入读取方法,所有输入校验和异常处理逻辑都集中在该类中;LoadDispatcher 类修改了排序算法,按照作业要求使用冒泡排序实现货物按编号升序排列;CargoCompartment 类新增了获取力臂的方法,以支持载重平衡计算。
本次作业的设计严格遵循了作业要求,未使用继承、多态和接口,所有类间关系均通过关联、组合和依赖实现。通过 SourceMontor 分析,本次作业所有方法的平均圈复杂度为 3.1,最高圈复杂度为 7(WeightBalanceCalculator 类的 generateLoadSheet 方法),整体代码复杂度控制合理。与前两次作业相比,本次作业的代码结构更加清晰,职责划分更加细致,系统的可维护性和可扩展性达到了较高水平。
二、采坑心得
在三次作业的编码和提交过程中,我遇到了许多问题,这些问题涵盖了输入处理、类设计、业务逻辑、算法实现等多个方面。通过对这些问题的分析和解决,我积累了宝贵的实战经验,也对面向对象设计和工程编码有了更深刻的理解。
2.1 输入处理相关问题
输入处理是 Java 控制台程序中最容易出现问题的环节,也是我在三次作业中踩坑最多的地方。
第一次作业中,我遇到了经典的 Scanner 换行符问题。在使用 nextInt () 方法读取整数后,缓冲区会残留一个换行符,后续调用 nextLine () 方法会直接读取到这个空换行符,导致程序无法正确读取后续的字符串输入。例如,在读取货物件数 n 后,调用 nextLine () 读取货物名称时,第一次读取到的总是空字符串。为了解决这个问题,我按照作业提示,在每次 nextInt () 之后额外调用一次 nextLine () 来消耗残留的换行符。但在测试过程中我发现,这种方法在输入格式不规范时依然会出现问题,例如输入整数后不小心输入了多个空格。
第二次作业中,我对输入处理方式进行了优化,统一使用 nextLine () 方法读取整行数据,然后通过 split () 方法拆分出所需的字段。这种方法彻底解决了换行符残留的问题,同时也提高了输入处理的灵活性。但在测试过程中,我又遇到了新的问题:当输入的字符串中包含多个连续空格时,split ("\s") 方法会生成空字符串元素,导致数组越界异常。通过查阅资料,我将 split 的参数改为 "\s+",该正则表达式可以匹配一个或多个连续的空白字符,完美解决了这个问题。
第三次作业中,输入格式更加复杂,包含了整数、浮点数、字符串等多种类型,同时要求对所有输入数值进行合法性校验。一开始,我将输入读取和校验逻辑分散在主方法的各个地方,导致代码冗余且难以维护。后来,我将所有输入读取和校验逻辑封装到 InputValidator 类中,提供了标准化的 readInt、readDouble 等方法,一旦检测到非法输入,立即输出提示信息并终止程序。这种设计不仅简化了主方法的逻辑,还确保了输入校验规则的统一性。但在测试过程中,我发现了一个边界问题:当输入的前舱货物数量 p 大于总货物数量 m 时,程序会在读取后舱货物时出现数组越界异常。为了解决这个问题,我在 InputValidator 类中新增了 readIntInRange 方法,专门用于读取指定范围内的整数,确保了输入数据的合法性。
2.2 类设计与职责划分问题
单一职责原则是本次作业的核心要求,但在实际编码过程中,我多次出现职责划分不清晰的问题。
第一次作业中,我最初的设计是将所有逻辑都放在 Main 类中,Cargo 和 Flight 类仅作为数据容器。这种设计虽然能够实现功能,但代码结构混乱,可读性和可维护性极差。例如,排序逻辑、总重量计算逻辑、输出逻辑都混杂在 Main 方法中,一旦需要修改输出格式,就需要在大量代码中查找对应的部分。后来,我按照 SRP 原则对代码进行了重构,将不同的职责拆分到不同的类中,代码质量得到了显著提升。
第二次作业中,我在设计 CargoCompartment 类时,最初将货舱查找逻辑也放在了该类中,导致 CargoCompartment 类不仅要管理自身的载重状态,还要负责查找其他货舱,违背了 SRP 原则。后来,我将货舱查找逻辑移至 LoadDispatcher 工具类中,使得 CargoCompartment 类的职责回归到管理单个货舱的状态,符合单一职责原则。
第三次作业中,我在设计 WeightBalanceCalculator 类时,最初的想法是让该类持有一个 Flight 对象作为成员变量,在构造器中初始化,然后通过成员方法进行计算。但仔细思考后发现,这种设计会导致 WeightBalanceCalculator 类变成有状态的类,每次计算不同的航班都需要创建新的对象,降低了工具类的复用性。后来,我按照作业要求,将 Flight 对象作为 generateLoadSheet 方法的参数传入,使得 WeightBalanceCalculator 类成为无状态的纯工具类,提高了代码的复用性和可测试性。
2.3 业务逻辑与算法实现问题
业务逻辑的准确性是系统的核心,在三次作业中,我多次因为对业务需求理解不透彻或算法实现错误导致测试点不通过。
第一次作业中,我在实现排序算法时,最初使用了冒泡排序,但在测试过程中发现,当货物数量较多时,排序效率较低。后来,我将排序算法改为选择排序,减少了交换的次数,提高了排序效率。同时,我还发现了一个逻辑错误:在排序时,我错误地将货物按重量升序排列,而作业要求是降序排列。通过调试和对比输出样例,我及时修正了这个错误。
第二次作业中,我在实现多维度超载判断时,最初只判断了单个货舱是否超载,而忽略了航班整体是否超过最大起飞重量和最大业载重量。后来,我仔细阅读了作业需求,在输出部分增加了航班整体配载状态的判断逻辑,区分了 “超过最大起飞重量”、“超过最大业载重量”、“两者均超” 三种情况,确保了业务逻辑的完整性。
第三次作业中,载重平衡计算是核心难点,涉及多个复杂的物理公式。在最初的实现中,我因为对公式理解错误,导致重心百分比的计算结果始终不正确。例如,我错误地将实际重心减去 MAC 后缘距离,而不是前缘距离,导致计算出的 CG% MAC 为负数。后来,我对照作业给出的公式,一步步拆解计算过程,将每个中间变量都打印出来进行调试,最终找到了错误并修正。此外,作业要求必须使用冒泡排序实现货物按编号升序排列,我在最初实现时,将排序方向搞反了,导致货物按编号降序排列。通过单步调试排序过程,我及时发现并修正了这个错误。
三、改进建议
虽然三次作业的代码都能够正确实现需求,但在代码质量、可扩展性、可维护性等方面仍有改进空间。以下是我针对三次作业代码提出的具体改进建议。
3.1 第一次作业改进建议

排序算法优化:目前使用的选择排序算法时间复杂度为 O (n²),虽然能够满足作业要求,但当货物数量较大时,性能会明显下降。在实际项目中,可以使用 Java 自带的 Collections.sort () 方法,该方法采用了优化的归并排序算法,时间复杂度为 O (n log n),性能更优。
输出逻辑解耦:目前的输出逻辑硬编码在 CargoSorter 类中,灵活性较差。可以将输出逻辑抽象为接口,提供不同的实现类,例如控制台输出实现类、文件输出实现类、HTML 输出实现类等,使得系统可以支持多种输出格式。
增加异常处理:目前的代码没有处理输入非数字字符的情况,当输入非法字符时,程序会直接抛出 InputMismatchException 异常。可以在 InputValidator 类中增加对输入类型的校验,捕获异常并输出友好的提示信息。

3.2 第二次作业改进建议

货舱查找优化:目前的货舱查找逻辑是通过遍历 List 实现的,时间复杂度为 O (n)。当货舱数量较多时,查找效率较低。可以使用 HashMap 来存储货舱对象,以货舱 ID 为 key,货舱对象为 value,将查找时间复杂度降至 O (1)。
输入校验扩展:目前的 InputValidator 类只提供了整数和浮点数的范围校验,功能较为单一。可以扩展更多的校验方法,例如字符串非空校验、字符串格式校验(如航班号格式)、货舱 ID 唯一性校验等,进一步提高输入数据的合法性。
增加日志记录:目前的系统没有日志记录功能,当出现问题时难以排查。可以引入日志框架(如 SLF4J+Logback),记录系统的运行状态、输入数据、计算结果等信息,方便问题排查和系统调试。

3.3 第三次作业改进建议

常量配置化:目前的系统常量(如空机重量、空机力臂、力臂参数、MAC 参数等)都硬编码在 WeightBalanceCalculator 类中,修改起来不方便。可以将这些常量提取到配置文件(如 properties 文件)中,系统启动时读取配置文件,使得常量的修改无需改动代码。
异常处理优化:目前的异常处理方式是直接调用 System.exit (0) 终止程序,这种方式比较粗暴,不利于单元测试和系统集成。可以自定义业务异常类,在检测到非法输入或业务错误时抛出对应的业务异常,在主方法中统一捕获并处理异常,提高代码的可测试性。
支持多货舱扩展:目前的 Flight 类只支持前后两个货舱,扩展性较差。可以将 Flight 类中的 front 和 back 属性改为 List,支持任意数量的货舱。同时,修改 WeightBalanceCalculator 类的计算逻辑,遍历所有货舱计算总重量和总力矩,使得系统可以支持不同型号的飞机。
增加单元测试:目前的代码没有单元测试,只能通过手动输入测试用例进行验证,效率低且容易遗漏边界情况。可以使用 JUnit 框架编写单元测试,对每个类的每个方法进行全面测试,确保代码的正确性和稳定性。

四、总结
通过本阶段三次迭代作业的学习和实践,我在面向对象设计、Java 编程能力、业务逻辑实现等方面都有了显著的提升。
首先,我深刻理解了单一职责原则的重要性。单一职责原则是面向对象设计的基础,它要求每个类只负责一个功能领域,只有一个引起它变化的原因。在三次作业中,我从最初将所有逻辑放在 Main 类中,到后来将职责逐步拆分到多个类中,代码的可读性、可维护性和可扩展性得到了质的提升。我认识到,好的代码不仅要能够实现功能,还要易于理解、易于修改、易于扩展。
其次,我掌握了迭代式开发的基本流程。三次作业采用了迭代式开发模式,每次作业都在前一次的基础上进行扩展和优化。这种开发模式使得复杂的系统可以被分解为多个简单的迭代,每个迭代都有明确的目标和交付物,降低了开发的难度和风险。同时,迭代式开发也使得代码可以逐步完善,每次迭代都可以对前一次的代码进行重构和优化。
再次,我提高了将业务需求转化为代码的能力。三次作业都基于真实的航空配载业务场景,需要将复杂的业务需求和物理公式转化为可执行的代码。在这个过程中,我学会了仔细分析需求,拆解业务流程,识别核心实体和业务逻辑,然后通过合理的类设计和方法设计实现需求。同时,我也认识到,准确理解业务需求是正确实现代码的前提,只有深入理解业务,才能设计出符合业务逻辑的系统。
最后,我认识到了输入校验和异常处理的重要性。一个健壮的系统不仅要能够处理正常的输入,还要能够优雅地处理各种非法输入和异常情况。在三次作业中,我从最初忽略异常处理,到后来逐步完善输入校验和异常处理逻辑,系统的鲁棒性得到了显著提升。我认识到,异常处理不是可有可无的,而是系统设计中不可或缺的一部分。
在未来的学习中,我还有许多需要进一步学习和研究的地方。首先,我需要深入学习设计模式,掌握更多的面向对象设计技巧,提高系统的设计能力。其次,我需要学习单元测试和集成测试的方法,养成编写测试代码的习惯,提高代码的质量和可靠性。再次,我需要学习大型系统的架构设计知识,了解如何设计和开发高并发、高可用、可扩展的大型系统。最后,我还需要深入学习航空配载的专业知识,了解更多的业务场景和行业标准,为今后从事相关领域的工作打下基础。
对于本课程的教学和作业组织,我有以下几点建议:第一,可以增加更多的真实业务案例,让学生了解面向对象设计在实际项目中的应用,提高学习的积极性和实用性。第二,可以增加代码评审环节,让学生互相评审代码,指出彼此的优点和不足,共同提高代码质量。第三,可以提供更多的设计指导,例如在作业开始前,讲解常见的设计误区和最佳实践,帮助学生更好地完成作业。第四,可以增加小组合作项目,让学生分组完成更复杂的系统,培养团队协作能力和沟通能力。
总之,本阶段的三次作业让我受益匪浅,不仅提高了我的编程能力和设计能力,还培养了我的工程思维和解决问题的能力。我会将本次作业中学到的知识和经验应用到今后的学习和工作中,不断提高自己的专业水平。

posted @ 2026-05-17 17:28  qiuchangliu  阅读(9)  评论(0)    收藏  举报