面向对象设计与构造 —— 第一单元总结

面向对象设计与构造 —— 第一单元总结
一、前言
本单元三次作业围绕“航空配载”迭代展开:第一次作业实现单航班基础货运与超载判断,知识点以基础类设计和语法为主,题量小、难度入门;第二次作业引入多货舱独立载重约束及整体起飞/业载校验,需处理关联关系与职责分离,题量和类间协作难度明显提升;第三次作业加入旅客行李并引入重心及%MAC载重平衡计算,要求整合多源数据并实现格式化报表,题量最大、业务计算复杂度最高。整体呈“基础语法→多模块协作→领域计算”的递进,梯度设计合理。
二、设计与分析
1.第一次作业
作业要求
设计基础航班货运配载模块,记录航班基本信息(航班号、最大载重),按货物重量从高到低添加货物,实时计算总重并判断超载。
实现方式
核心类 Flight 持有航班号、最大载重及 ArrayList,提供总重计算与超载判断。Cargo 仅含名称和重量。CargoSorter 工具类执行选择排序(降序),LoadManifest 负责先排序再批量装载。职责较分明,数据结构以 ArrayList 为主,耦合度较低。

image

image

Main 程序入口,读取航班号、最大载重、货物信息,创建 Flight 和 LoadManifest,调用装载流程并输出结果
Flight 存储航班号、最大载重、货物列表;提供总重计算 CargosTotalWeight() 和超载判断 isOverLoad() 方法
Cargo 数据类,存储货物名称和重量,提供对应的 getter/setter
CargoSorter 工具类(私有构造),提供静态方法 sortByWeight(),使用选择排序按重量降序排列货物列表
LoadManifest 装载清单类,关联 Flight,负责调用排序工具并将排序后的货物批量添加至航班

心得
1.亮点:所有文件圈复杂度 ≤5,结构健康;每个.java文件只定义 1 个类,符合单一职责原则;Main.java注释率 21.2%,习惯良好。
2.待改进:Flight和Cargo中存在多个未被调用的 setter 及冗余方法;main方法逻辑堆积(19 条语句),后续应拆分为独立步骤方法;部分文件注释率为 0。
3.小结:本次作业设计合规,但已暴露出接口过度提供和主函数过长的苗头,为后续迭代埋下优化空间。
2.第二次作业
作业要求
飞机分多个货舱,每个货舱有独立最大载重和网格位置。货物指定目标货舱,按重量降序装载,实时检查货舱超载,同时校验航班整体最大起飞重量与最大业载重量。
实现方式
Flight 关联多个 CargoCompartment,每个货舱维护自己的货物列表和位置信息。LoadDispatcher 提供排序与查找方法。主流程先对所有货物降序排序,逐一尝试装入指定货舱(超重则失败提示),最后汇总各舱重量做整体约束判断。类间通过聚合关系协作,职责分离更明确。

image

image

Main 读取航班基本信息、多个货舱数据(含位置网格)、多件货物(含目标货舱);调用排序,逐件尝试装入指定货舱,输出各舱装载状态及整体超载判断
Flight 存储航班号、最大起飞重量、最大业载重量及多个货舱列表;提供 calculateTotalCargoCompartment() 计算所有货舱总重
CargoCompartment 存储货舱 id(String)、最大载重、网格位置、货物列表;提供 addCargo() 方法(超重返回 false)和当前总重获取
Cargo 存储货物 id、重量和目标货舱标识,提供 getter/setter
Position 存储货舱网格的行列值,提供 getPosName()、getRow()、getCol()
LoadDispatcher 工具类,提供静态方法 sortByWeight()(选择排序降序)和 FindCargo()(按 id 查找货物索引)
InputValidator 定义了 isValidInt() 和 isValidDouble() 静态校验方法,但本次作业中未被实际调用

心得
1.亮点:所有文件圈复杂度仍 ≤5,分支清晰;新增辅助类职责单一,模块化提升;注释覆盖率提高。
2.待改进:InputValidator 整类未用、Position 功能未实际调用,形成“僵尸代码”;
主函数语句数膨胀至 42 条;LoadDispatcher 分支占比 28.6%,可进一步拆分。
3.小结:本次作业的冗余问题从“多余方法”升级为“整类未用”,说明设计阶段需从实际调用出发,不能为符合题目要求而预先构建“万能”类。
3.第三次作业
作业要求
加入旅客及行李重量,进行严格的载重平衡计算(重心、%MAC),输出格式化舱单并评估配载安全。
实现方式
新增 WeightBalanceCalculator 封装物理计算常量与方法,LoadSheetPrinter 负责格式化输出,Passenger 和 Luggage 建模旅客数据。Flight 仅作为数据容器,所有计算移交工具类。主程序通过 InputValidator 统一校验输入,形成“数据装配—计算—输出”三层分离,架构清晰。

image

image

Main 通过 InputValidator 统一读取航班号、前后舱尺寸/载重、旅客及行李、货物分配;创建所有对象,调用 LoadSheetPrinter 打印完整载重平衡舱单
InputValidator 封装 Scanner,提供 getString()、getInt()、getNonNegativeInt()、getDouble() 等静态方法,含范围校验与非法输入时的退出处理
Flight 存储航班号、多个货舱列表、旅客列表;仅提供 getter,所有计算移交工具类
CargoCompartment 存储货舱 id(int)、最大载重、货物列表;addCargo() 超重时直接警告并退出程序;提供当前总重和货物列表获取
Cargo 存储货物 id(int)和重量,仅提供 getter
Passenger 存储旅客总重(固定 75kg 体重 + 行李重量)及 Luggage 对象,提供总重获取
Luggage 存储行李重量
Position 存储货舱网格行列,提供 getPosName(),但本次未被实际使用
LoadDispatcher 工具类,提供冒泡排序 sortByWeight()(原地降序)和 FindCargo(),但 Main 未调用
WeightBalanceCalculator 核心计算工具,封装空机重量、各站位力臂常量;提供总重、总力矩、实际重心、%MAC 及配载安全评估等静态方法
LoadSheetPrinter 舱单打印工具,提供 printLoadSheet() 静态方法,内部分段打印基础数据、旅客数据、货物数据、汇总计算与配平评估

心得
1.亮点:复杂度保持 ≤5,主函数语句数降至 32 条,分支占比 9.4%,方法平均语句数 4.7,粒度合理;模块化程度为三次最佳,注释率提升至 50%。
2.待改进:LoadDispatcher、Position 成为遗留代码;WeightBalanceCalculator 方法单一(仅一个方法),应拆分为多个独立计算步骤;部分实体类仍无注释。
3.小结:本次作业接近工业级入门水平,但遗留代码问题凸显——当代码质量从“及格”走向“优秀”时,需要勇气做减法,定期清理不再使用的旧代码。
三、本单元踩过的坑与改进方向
本单元迭代中暴露的问题具有普遍性,归纳如下:
坑1:过度提供接口(违背 YAGNI 原则)
表现:Flight 中未调用的 add/remove/setter 方法、InputValidator 整类闲置、LoadDispatcher 遗留。
影响:代码膨胀,阅读混淆,setter 破坏不可变性。
坑2:主函数堆积
表现:三次作业 main 语句数依次为 19、42、28,全部逻辑堆在入口方法中。
影响:Main 职责过重,难以单独测试步骤,扩展只能追加代码。
坑3:遗留代码不清理
表现:LoadManifest 在第二次被移除,但 LoadDispatcher、Position 在第三次仍保留。
影响:新旧代码混杂,SourceMonitor 统计数据失真,潜在误用风险。
坑4:工具类职责混乱
表现:LoadDispatcher 既排序又查找,WeightBalanceCalculator 所有计算堆在一个方法内。
影响:测试粒度粗,方法复用率低,静态方法导致高耦合。
坑5:输入校验与业务耦合,健壮性不足
表现:前两次无校验,第三次校验失败直接 System.exit(0)。
影响:类型错误导致异常崩溃,无法捕获处理,测试困难。
坑6:注释分布不均
表现:实体类注释率始终为 0,工具类注释尚可。
影响:业务模型含义需靠猜测,API 文档化程度低。
image

四、改进建议
针对上述六个问题,提炼出以下通用改进方向,适用于后续所有迭代开发任务:
1.建立“按需编码 + 提交前清理”的双重机制
从源头控制冗余代码的产生:编码阶段严格遵守 YAGNI 原则,只编写当前确实被调用的方法和类,不为“可能用到”提前铺设接口。提交前使用 IDE 的“查找引用”功能逐类排查,将未被调用的方法、字段、类一律删除。每次功能迭代结束后,将清理遗留代码作为固定步骤,纳入个人开发流程。衡量标准:提交后 SourceMonitor 报告的“死代码”指标应为零。
2.强制拆分主函数,入口方法不超过 10 行
将 main 方法定位为“调度中心”而非“业务车间”,采用模板化结构:
java
public static void main(String[] args) {
readInput();
buildModel();
process();
printResult();
}
每个步骤方法独立承担一项职责,可单独测试、单独修改。迭代新增功能时,通过扩展步骤方法或新增步骤实现,而不是向 main 内部追加代码。
3.规范工具类设计,一方法一功能
工具类应遵循“高内聚、低耦合”原则:每个工具类只负责一类功能(如排序、查找、校验、格式化分离),类内每个方法只完成一个可独立测试的计算步骤。常量与计算逻辑分离,将物理参数、配置值抽取为独立的常量类或枚举,避免与业务逻辑混合。静态方法仅用于无状态的纯函数场景,若未来可能需要替换实现,应改用接口 + 实现类的方式预留扩展点。
4.统一输入校验与异常处理策略
所有用户输入统一经过 InputValidator 处理,禁止在业务代码中直接使用 Scanner。校验失败时抛出明确的业务异常(如 IllegalArgumentException),由 main 方法统一 try-catch 捕获并输出友好错误信息,杜绝使用 System.exit(0) 直接终止程序。这样既保证错误信息的一致性和可读性,也便于单元测试验证异常分支。

五、总结
学到了什么
面向对象设计应基于需求而非题目字面,不能为“完整性”过度编码,需遵循 YAGNI 原则,及时清理无用代码。
代码可读性至关重要——即使方法简短,每个方法至少应有功能注释,复杂逻辑需分段说明。
设计应从实际调用场景出发,类之间协作关系比类数量更重要,避免出现“只定义不调用”的孤岛。
学会了使用 SourceMonitor 量化评估代码质量,并能够通过类图梳理架构。
需要进一步研究的内容
系统化的架构分层(如 MVC),在编码前先进行职责划分,避免随意新增类导致逻辑混乱。
面向对象核心特性(继承、多态、接口)的应用,这是区分面向过程与面向对象的关键,也是下单元重点。
自动化单元测试与重构技巧,提升代码验证效率和设计演进能力。
对课程组织的建议
作业迭代中可减少误导性的错误指引(如故意要求错误的排序方式),使测试点更贴合真实业务逻辑,避免学生因揣摩题意而偏离合理设计。
建议提供标准测试集或代码设计互评环节,帮助同学从功能和设计两个维度验证作业。
希望实验系统可以开放复制粘贴权限,或者尽快完善好试验系统的快捷编辑提示,每次花太多时间在没意义的编码上,很消耗时间,我需要花很多时间在凌云班的项目上,时间真的很不够用
下单元学习计划
重点学习继承、多态、接口等面向对象核心机制,尝试在表达式求导等场景中应用工厂模式、策略模式,逐步形成更规范的编码习惯。

标签
Java, 面向对象, 航空配载, 载重平衡, 代码复杂度, SourceMonitor, UML类图, Mermaid, 迭代开发

posted @ 2026-05-16 10:31  TheFuSheng  阅读(9)  评论(0)    收藏  举报