blog
面向对象设计与构造 —— 航空货运配载单元三次作业总结
写在前面
面向对象设计与构造课程第一单元以航空航班货物配载、多货舱管理、载重重心平衡为核心场景,布置了三次迭代式编程作业。从最基础的单航班单货物载重统计,到多货舱货物智能分配装载,再到融合旅客、行李、前后双货舱的航空重心配平系统,三次作业难度层层递进、功能逐步迭代。
通过本单元学习,我熟练掌握了 Java 基础语法与面向对象核心编程思想,建立起类与对象、封装、组合聚合的设计思维;理解了代码可扩展性、高内聚低耦合的工程设计理念;学会使用 SourceMonitor 进行代码复杂度度量、借助类图分析程序结构,同时积累了程序调试、边界测试、错误排查的实战经验。三次作业不仅夯实了编码能力,更让我体会到面向对象思想在实际业务系统开发中的核心价值。
Complexity Metrics(复杂度分析概念)
在进行作业代码分析前,先明确 SourceMonitor 复杂度度量核心指标,用于后续三次作业方法与类的复杂度评估:
- v (G) 循环复杂度:代表程序所有分支路径的数量,数值越大方法逻辑越繁琐,常规合理值应控制在 10 以内。
- ev (G) 基本复杂度:衡量程序结构化程度,规避冗余分支与嵌套,值越低代码结构越清晰、无冗余逻辑。
- iv (G) 设计复杂度:表征当前方法与其他方法的调用耦合程度,数值越高耦合性越强,代码越难维护与拓展。
- OCavg、WMC:分别为类的平均方法复杂度、类总方法复杂度,用于评估整个类的设计臃肿程度。
第一次作业
作业要求
设计航班与货物基础类,实现货物信息封装、按重量降序冒泡排序、航班总载重计算、最大载重对比及超载状态判断,完成输入解析、数据处理与格式化输出。
实现方式
采用类封装 + 数组存储的基础设计模式。定义Cargo货物类封装货物名称与重量私有属性,提供 getter 方法;Flight航班类维护航班号、最大载重、货物数组与有效货物数量,内置货物添加、冒泡降序排序、总重量累加、超载判断等核心方法;Main主类负责控制台输入、对象实例化、方法调用与结果打印。整体采用顺序式逻辑,依托数组固定长度存储货物,完成基础业务流程。
代码规模
通过 SourceMonitor 统计本次作业代码规模:
表格
Source File | Total Lines | Source Code Lines | Source Code Lines[%] | Comment Lines | Blank Lines |
|---|---|---|---|---|---|
Main.java | 42 | 38 | 90% | 2 | 2 |
Cargo.java | 36 | 32 | 89% | 2 | 2 |
Flight.java | 128 | 115 | 90% | 6 | 7 |
Total | 206 | 185 | 89% | 10 | 11 |
整体代码共 206 行,有效代码 185 行,注释与空行占比合理,代码精简无冗余,符合基础面向对象编程规范。
类图设计
本次作业采用基础三层类结构:
- Cargo 货物类:私有成员变量 cargoName、cargoWeight;构造方法、属性 getter 方法。
- Flight 航班类:私有成员 flightNo、maxWeight、cargoArray 数组、count 计数;提供 addCargo、sortByWeightDesc、getTotalWeight、isOverWeight 等业务方法。
- Main 主类:仅包含 main 方法,负责程序入口、输入处理、对象调度与结果输出。
类之间为组合关系,Flight 聚合多个 Cargo 对象,Main 作为调度者调用两大核心类,结构简单清晰,无多余依赖。
复杂度分析
第一次作业核心方法复杂度指标如下:
表格
方法 | ev(G) | iv(G) | v(G) |
|---|---|---|---|
Main.main | 1 | 2 | 2 |
Flight.addCargo | 1 | 1 | 1 |
Flight.sortByWeightDesc | 2 | 3 | 5 |
Flight.getTotalWeight | 1 | 2 | 2 |
Flight.isOverWeight | 1 | 1 | 1 |
Cargo.getter 方法 | 1 | 1 | 1 |
平均值 | 1.15 | 1.75 | 2.25 |
从复杂度数据可以看出,整体方法复杂度极低,仅冒泡排序方法sortByWeightDesc因双层循环与条件判断 v (G) 达到 5,仍在合理范围。所有方法 ev (G) 接近 1,说明代码结构化良好;iv (G) 普遍偏低,类与方法间耦合度小。缺点在于采用固定长度数组存储货物,扩展性差,后续新增货物数量时需修改代码。
Bug 分析
公测问题
公测中无逻辑正确性错误,输出格式基本符合要求,但存在货物排序边界处理不足问题:当货物数量为 0 或 1 时,冒泡排序仍执行循环,虽不影响结果但存在冗余运算。
自测与采坑
- 数组越界异常:最初冒泡排序循环边界未设置count-1,遍历数组全部空位导致访问 null 元素报错。
- 总重量计算错误:未按有效数量 count 遍历,直接遍历数组长度,累加 null 对象重量引发异常。
- 输出格式精度问题:未使用 printf 格式化保留 1 位小数,导致输出结果格式不统一。
测试方法
手动构造测试用例:正常多货物、单货物、零货物、刚好满载、轻微超载 5 类场景,逐行核对排序结果、总重量、超载提示,覆盖基础边界条件。
第二次作业
作业要求
在第一次作业基础上迭代升级,新增多货舱、货舱位置管理、货物指定目标货舱、按重量降序自动配载、装载失败提示、多货舱载重状态独立评估、航班整体载重校验功能,引入集合存储与工具类解耦设计。
实现方式
摒弃固定数组,改用ArrayList 动态集合存储货舱与货物;拆分单一职责类,新增Position位置类管理货舱行列坐标、LoadDispatcher货物调度排序类、InputValidator输入校验工具类;CargoCompartment货舱类独立管理单个货舱的最大载重、当前载重与货物列表;Flight航班类统一管理多个货舱,提供按 ID 查找货舱、统计全航班货物总重方法。采用面向对象分工设计,将排序、输入校验、位置描述等功能剥离为独立工具类,实现高内聚低耦合。
代码规模
本次作业代码规模显著提升,模块拆分更细致:
表格
Source File | Total Lines | Source Code Lines | Source Code Lines[%] | Comment Lines | Blank Lines |
|---|---|---|---|---|---|
Main.java | 85 | 78 | 92% | 3 | 4 |
Position.java | 32 | 28 | 87% | 2 | 2 |
Cargo.java | 40 | 35 | 88% | 3 | 2 |
CargoCompartment.java | 76 | 69 | 91% | 4 | 3 |
Flight.java | 68 | 62 | 91% | 3 | 3 |
LoadDispatcher.java | 28 | 25 | 89% | 1 | 2 |
InputValidator.java | 24 | 21 | 87% | 1 | 2 |
Total | 353 | 318 | 90% | 17 | 18 |
代码总量增至 353 行,拆分 7 个独立类,每个类仅负责单一功能,代码复用性大幅提升,注释分布均匀,可读性强。
类图设计
本次作业类结构更加模块化,核心关系如下:
- 基础实体类:Position(位置属性)、Cargo(货物属性);
- 容器类:CargoCompartment包含多个 Cargo 与 Position,Flight聚合多个 CargoCompartment;
- 工具类:LoadDispatcher负责货物排序、InputValidator负责数据合法性校验;
- 调度入口:Main处理输入、调用工具类完成排序配载、输出各货舱及航班状态。
类之间依赖关系清晰,工具类与实体类解耦,修改排序规则或校验规则无需改动核心业务类,可扩展性明显优于第一次作业。
复杂度分析
第二次作业核心方法复杂度:
表格
方法 | ev(G) | iv(G) | v(G) |
|---|---|---|---|
Main.main | 2 | 4 | 5 |
LoadDispatcher.sortCargos | 1 | 2 | 2 |
CargoCompartment.addCargo | 1 | 2 | 3 |
CargoCompartment.getCurrentWeight | 1 | 1 | 1 |
Flight.findCompartmentById | 2 | 3 | 4 |
InputValidator 校验方法 | 1 | 1 | 1 |
平均值 | 1.38 | 2.15 | 2.76 |
整体复杂度小幅上升,但仍处于合理区间。Flight.findCompartmentById因遍历集合 + 条件判断复杂度稍高,Main.main因输入处理、空行过滤、字符串拆分逻辑分支较多 v (G) 为 5。相较于第一次作业,通过工具类拆分复杂逻辑,避免了单个方法臃肿,设计耦合度依然较低。
Bug 分析
公测问题
公测无逻辑错误,所有配载、载重计算、状态判断均正确,但存在输入兼容不足问题:无法自动处理多行空输入、多空格分隔数据,部分特殊格式输入导致拆分失败。
互测与自测 Bug
- 输入读取异常:使用split(" ")无法匹配多个连续空格,导致货物、货舱参数解析错乱。
- 空行干扰:复制粘贴输入存在空行,直接读取会抛出数字转换异常。
- 货舱载重累加错误:初次设计未封装getCurrentWeight,每次遍历货舱货物重复计算,冗余且易出错。
- 货物装载判断失误:未提前校验剩余载重,直接添加货物后再判断,导致无效数据存入集合。
测试方法
采用边界测试 + 格式测试:构造多空格、空行、零货舱、零货物、货舱刚好满载、多货物跨货舱分配等用例;同时模拟错误输入格式,验证程序容错能力。
第三次作业
作业要求
在前两次基础上全面升级为航空完整载重平衡系统,新增旅客与行李管理、前后双独立货舱、货物按 ID 升序排序、航空力矩与重心百分比计算、起飞重量 / 业载双重超载判断、重心安全范围评估、完整载重平衡舱单输出,同时增加全量输入负数合法性校验。
实现方式
采用多层组合 + 静态常量工具类设计。拆分Luggage行李类、Passenger旅客类(内置标准体重、关联行李);CargoCompartment分为前后舱,新增货物 ID 冒泡排序;Flight关联旅客列表与前后双货舱;WeightBalanceCalculator作为静态计算工具类,封装空机重量、力臂、重心安全范围等常量,实现力矩、重心百分比、配平安全评估的核心算法。主类增加全流程输入负数校验,拦截非法数据,整体实现业务逻辑与算法计算完全分离。
代码规模
第三次作业功能最复杂,代码量与类数量达到峰值:
表格
Source File | Total Lines | Source Code Lines | Source Code Lines[%] | Comment Lines | Blank Lines |
|---|---|---|---|---|---|
Main.java | 156 | 142 | 91% | 8 | 6 |
Luggage.java | 28 | 24 | 86% | 2 | 2 |
Passenger.java | 45 | 40 | 89% | 3 | 2 |
Cargo.java | 32 | 29 | 90% | 1 | 2 |
CargoCompartment.java | 86 | 79 | 92% | 4 | 3 |
Flight.java | 72 | 65 | 90% | 4 | 3 |
WeightBalanceCalculator.java | 118 | 107 | 91% | 7 | 4 |
Total | 543 | 486 | 90% | 29 | 28 |
总代码 543 行,拆分 7 个核心类,涵盖实体、容器、算法计算、调度入口全模块,业务逻辑分层明确,算法与数据完全解耦。
类图设计
本次作业类结构最复杂,完整体现面向对象组合与单一职责:
- 基础实体:Luggage(行李重量)、Passenger(旅客 + 组合行李)、Cargo(货物 ID + 重量);
- 容器管理:CargoCompartment(前后舱、货物排序、装载校验)、Flight(聚合旅客、前后双货舱);
- 算法工具:WeightBalanceCalculator(静态常量、力矩计算、重心评估、舱单打印);
- 程序入口:Main(输入校验、对象初始化、货物分配、调用计算方法)。
类之间多层组合嵌套,每个类各司其职,静态工具类无需实例化即可调用,适合固定算法与常量管理,完全符合工程化设计思想。
复杂度分析
第三次作业核心方法复杂度显著提升:
表格
方法 | ev(G) | iv(G) | v(G) |
|---|---|---|---|
Main.main | 3 | 6 | 8 |
CargoCompartment.sortCargo | 2 | 3 | 5 |
WeightBalanceCalculator.calculate | 2 | 5 | 7 |
WeightBalanceCalculator.print | 4 | 8 | 11 |
Passenger.getTotalWeight | 1 | 1 | 1 |
平均值 | 2.1 | 3.8 | 5.6 |
本次因重心计算、舱单格式化输出、全量输入校验包含大量分支条件,部分方法 v (G) 接近 10 临界值。print方法因多模块数据打印、格式拼接、状态判断,循环与分支较多,复杂度最高。整体 ev (G) 与 iv (G) 同步上升,主要是业务逻辑复杂化、方法调用层级增多导致,但仍通过类拆分避免了单个方法逻辑过度臃肿。
Bug 分析
公测问题
公测无功能性 Bug,载重计算、重心百分比、安全状态判断均准确,但舱单格式排版不够规整,冗余空格较多,细节优化不足。
自测采坑
- 重心公式计算错误:混淆 MAC 前缘、力臂参数,导致重心百分比偏差,安全判断失效。
- 负数输入未拦截:初期未校验旅客数、重量、货舱行列负数,出现非法业务数据。
- 货物排序错乱:冒泡排序 ID 升序条件写反,货物输出顺序颠倒。
- 前后舱货物分配越界:未校验前舱货物数量区间,出现分配数量超出总货物数的逻辑漏洞。
- 浮点数精度误差:直接使用 double 累加重量,部分场景出现微小精度偏差。
测试方法
采用全场景覆盖测试:零旅客、零货物、单货舱满载、重心临界值(25%、38%)、超出起飞重量、超出业载重量、负数非法输入等多类测试用例,手动核对力矩、重心百分比、配平状态,确保算法与逻辑双重正确。
采坑心得
纵观三次作业迭代,我在编码与调试过程中遇到的问题具有很强的共性,也积累了详实的实战心得:
边界条件是 Bug 高发区:三次作业均在循环边界、数组 / 集合遍历范围、数值正负、临界载重、极值数据上出现错误。例如冒泡排序循环边界、有效货物数量遍历、重心安全临界值判断,忽略边界极易引发越界、计算错误、逻辑失效。后续编码必须先梳理边界场景,再编写核心逻辑。
输入处理与数据校验不可忽视:从第二次作业多空格、空行解析失败,到第三次作业负数非法输入,让我意识到程序不能依赖输入格式完美,必须主动做trim 去空、多空格拆分、数值范围校验、负数拦截,提升程序健壮性。
面向对象设计拆分至关重要:第一次作业所有逻辑集中在主类与航班类,臃肿难维护;后两次通过工具类拆分、单一职责拆分、算法与数据分离,代码可读性、可维护性大幅提升。若前期类结构设计混乱,后期新增功能会层层嵌套,复杂度飙升。
复杂度控制需提前规划:SourceMonitor 分析显示,复杂 Bug 多集中在 v (G) 过高的方法中,大量 if-else 嵌套、多层循环会让调试难度倍增。学会将复杂方法拆分为多个子方法,降低单个方法分支数量,是减少 Bug 的关键。
测试不能仅依赖样例:官方样例仅覆盖常规场景,大量边界、非法格式、临界值 Bug 需要手动构造测试用例才能发现。三次作业互测与自测经验表明,手动造针对性测试用例比盲目自动化测试更高效。
改进建议
代码结构改进
- 引入继承与抽象类:可设计WeightItem抽象父类,封装货物、旅客通用重量属性,减少重复代码;前后货舱可继承统一货舱父类,复用装载、排序方法。
- 统一异常处理:摒弃 if 判断拦截错误、直接退出程序的方式,改用try-catch捕获输入格式异常、数值非法异常,自定义异常提示信息,程序更优雅健壮。
- 拆分超长方法:第三次作业 main 方法、print 打印方法代码过长,可按功能拆分为输入初始化、货物装载、重心计算、舱单输出多个子方法,降低复杂度。
功能逻辑改进
- 优化装载算法:目前货物固定分配前后舱,可改进为自动最优配载,根据货舱剩余重量自动分配货物,最大化利用载重空间。
- 增加数据持久化:将航班配载舱单保存至本地文本文件,支持查询历史配载记录,拓展实用性。
- 完善输出格式:优化舱单排版,对齐文字与数值,去除冗余空格,模拟真实航空载重平衡单据格式。
代码规范与复杂度改进
- 统一命名与注释:规范类、方法、变量命名风格,为核心算法、复杂分支逻辑添加注释,便于后续阅读与修改。
- 替换低效排序:手动冒泡排序可替换为Collections.sort工具排序,简化代码同时提升效率。
- 常量配置分离:将航空力臂、安全重心范围、空机重量等常量单独放入常量类,统一管理,修改参数无需改动业务代码。
总结
知识与能力收获
经过三次航空货运配载作业迭代,我系统掌握了 Java 面向对象编程核心知识点:熟练运用类与对象、封装、组合聚合、构造方法、getter/setter;掌握数组与 ArrayList 集合的使用场景与差异;学会冒泡排序、累加统计、力矩重心等算法逻辑;能够使用 SourceMonitor 分析代码复杂度、借助类图梳理程序结构;养成了先设计类结构、后编码、重视边界测试的编程习惯。
同时,在工程思维上有明显提升:理解了单一职责、高内聚低耦合的设计原则,懂得模块化拆分代码、解耦业务逻辑与工具算法,不再局限于实现功能,更追求代码的可读性、可扩展性与可维护性。
待提升方向
- 对继承、多态、接口、设计模式掌握不足,三次作业未用到高级面向对象特性,后续需深入学习工厂模式、策略模式,优化复杂系统设计。
- 异常处理、泛型、文件 IO 等进阶知识应用欠缺,代码容错性与拓展性仍有提升空间。
- 算法优化能力不足,排序、遍历逻辑仍停留在基础实现,缺乏效率优化思维。
课程与作业改进建议
- 作业设计:三次作业迭代式设计非常合理,从基础到综合贴合实际业务,建议继续保持梯度递进模式,增加少量设计模式相关作业场景。
- 课堂教学:建议增加 UML 类图绘制、SourceMonitor 复杂度分析实操教学,讲解如何通过类图提前规划代码结构,减少后期重构成本。
- 实验与互测:可增加课堂代码互评环节,让同学互相阅读代码、分析设计优劣,借鉴不同编程思路;同时布置专项边界测试练习,培养测试思维。
- 作业指导:针对复杂业务算法(如重心计算),可提供原理讲解与公式说明,降低理解难度,更专注于面向对象设计实现。
浙公网安备 33010602011771号