NCHU--OOP--BLOG2--航空货运管理系统

题目集 8-9 航空货运管理系统实践与反思

一、前言

    在前两轮 PTA 作业中,我们已完成了两道航空货运管理系统的开发。这两个系统延续了迭代优化的思路,与此前的项目形成了有机的演进脉络。值得注意的是,相较于上一次作业,此次在算法实现的复杂度上有明显下降,但在代码设计层面却提出了全新挑战 —— 题目核心聚焦于面向对象七大原则的实践应用。这意味着本次开发的重点不再是算法攻坚,而是对抽象类设计、继承与多态机制、合成复用原则等面向对象核心知识的深度落地,在系统架构设计维度实现了难度的跃升。

二、设计与分析

题目集8单部电梯调度问题分析

题目如下:


需要生成如下格式订单:

客户:姓名(电话)订单信息如下:


航班号:
订单号:
订单日期:
发件人姓名:
发件人电话:
发件人地址:
收件人姓名:
收件人电话:
收件人地址:
订单总重量(kg):
微信支付金额:

货物明细如下:


明细编号 货物名称 计费重量 计费费率 应交运费
1 ...
2 ...

SourceMonitor报表分析

类图分析

分析与心得

一、求和操作符误用导致的总量计算异常

在计算多件货物总重量时,误将累加操作符+=写成赋值操作符=,代码如下:

for (Goods goods : goodsList) {
totalWeight = goods.getWeight(); // 错误:应为 totalWeight += goods.getWeight();
}

二、类图设计原则总结
  • 单一职责(SRP):各分类功能独立(航班、货物、支付等模块职责明确)。
  • 依赖倒置(DIP)Order 依赖 Payment 抽象,而非具体支付类(如 WeChatPay)。
  • 开闭原则(OCP):支付方式通过接口扩展(新增支付类不影响订单逻辑)。
  • 里氏替换(LSP)Sender/Receiver 继承 Person,可替换父类使用(行为兼容)。
  • 接口隔离(ISP)Payment 接口仅含支付核心方法(避免冗余,专注单一职责)。

架构价值:可维护(模块职责清晰,修改风险低)、可扩展(新增功能快速迭代,无需改动核心逻辑)。

三、代码质量的深度剖析

通过 SourceMonitor 生成的代码分析报告,可以清晰地看出我提交的代码虽然通过了测试点,但在质量上存在不少问题:

一、代码规模方面
  1. 方法设计较为合理

    • 平均每个方法包含3.3条语句,基本做到了功能单一,例如Goods.getWeight()这类方法专注于数据获取,没有过多冗余操作。
    • 最大方法复杂度为5(Goods.getRate()),在处理费率分段计算时,逻辑层次相对清晰,没有出现过于复杂的嵌套结构。
  2. 代码结构紧凑

    • 484行代码包含263条可执行语句,代码密度适中,没有明显的空行或无效注释,整体结构较为简洁。
    • 类与接口数量控制在3个,在项目初期阶段,这种设计有助于快速实现功能,减少不必要的抽象层级。
二、代码质量层面
  1. 逻辑控制较为清晰

    • 分支语句占比5.3%,主要集中在核心业务逻辑(如费率计算),且嵌套层级较浅(最大深度3层),流程控制基本合理。
    • 方法间调用关系明确,例如Order.getTotalMoney()依赖Goods.getBasicMoney(),模块间协作关系较为直观。
  2. 遵循基础编码规范

    • 采用标准的Java Bean命名规范(getter/setter方法),如Goods.getName()Order.setOrderID(),代码可读性较好。
    • 多数方法圈复杂度为1(如Agent.getCustomer()),实现了简单功能的快速封装。
三、类设计角度
  1. 组合关系的基本应用

    • Order类通过getGoods()方法持有Goods实例,建立了“订单-货物”的组合关系,避免了过度继承带来的耦合问题。
    • Agent类封装了客户、航班等信息的获取逻辑,通过属性注入的方式实现了基本的依赖管理。
  2. 功能模块化的初步尝试

    • Goods类将货物属性(长宽高、重量)与基础计算(计费重量、运费)封装在一起,虽然存在一定职责重叠,但实现了基础功能的内聚。
    • Order.show()方法集中处理报表展示逻辑,调用getTotalMoney()等计算方法,体现了“数据处理与展示分离”的初步意识。
  3. 扩展性的潜在可能

    • 费率计算采用分段函数(if-else)实现,虽然目前未完全符合开闭原则,但逻辑集中在Goods.getRate()方法中,为后续重构预留了一定空间。
    • 类间通过方法调用而非直接属性访问(如Order.getAgent()),保留了接口抽象的可能性。
四、数据交互合理性
  1. 输入输出流程清晰

    • Order.show()方法通过多次调用其他方法(如getTotalWeight())完成数据聚合与展示,流程较为清晰,符合基本的业务逻辑。
    • Goods.getChargeableWeight()方法正确实现了“体积重量=长×宽×高/6000”的业务公式,计算逻辑准确。
  2. 数据封装基本到位

    • 所有属性通过getter/setter方法访问,确保了数据的封装性,例如Goods.setWeight()方法对属性的访问控制。
    • Order类通过getTotalWeight()方法聚合多件货物的重量,保证了数据计算的一致性。

测试通过截图

题目集9单部电梯调度问题分析

题目如下:




生成的订单同上次

SourceMonitor报表分析

类图分析

分析与心得

一、代码规模
  • 行数:Main.java有 573 行代码,从规模上看不算特别少。但注释率仅 0.7%,这意味着代码中对核心逻辑的解释极度匮乏。像货物重量计算、航班承载判断等关键业务逻辑处,缺乏注释会让后续接手代码的人一头雾水。
  • 分支与调用:分支语句占比 9.0% ,要留意那些嵌套深度超过 3 层的分支结构,过深的嵌套往往意味着逻辑过于复杂,可维护性差。有 75 个方法调用,我们得审视每个方法是否真的做到了职责单一,有没有一个方法干了多件事的情况。
二、代码质量
  • 分支优化:查看Block Histogram 发现部分代码块深度较大。比如在一些涉及业务规则判断的地方,可能存在多层if - else嵌套。可以把这些复杂的嵌套逻辑提取出来,封装成独立的方法,让代码逻辑更清晰。
  • 注释补充:对于像Flight.canCarry(判断航班能否承载货物)、Payment.pay(支付操作)等核心逻辑方法,必须添加注释。注释要说明方法的功能、输入参数的含义、返回值的意义等,方便他人理解和维护代码。
    三、类设计(结合类图分析)
  • 单一职责原则(SRP:Goods类负责货物相关属性和操作,Flight类管理航班信息,Payment类处理支付,职责相对清晰。但Order类可能承担了过多职责,比如既负责订单数据管理,又进行一些复杂的计算逻辑。可以考虑把计算逻辑分离出来,比如单独创建一个OrderCalculator类来专门处理订单的计算,让Order类专注于订单数据的存储和基本操作。
  • 依赖倒置与开闭原则(DIP/OCP):支付模块设计得比较好,通过接口来定义支付行为,WeChatPay、CashPay等具体支付方式去实现这个接口。这样如果要新增支付方式(比如添加一种新的电子钱包支付方式),不需要修改Order类等高层模块的代码,符合开闭原则。对于Flight类的载重规则,也可以进一步接口化,方便未来根据不同的业务场景(比如不同的航班类型有不同载重标准)进行扩展。
  • 里氏替换与接口隔离原则(LSP/ISP):Sender和Receiver继承自Person,在使用上可以互相替换,并且行为保持一致,满足里氏替换原则。Payment接口定义得比较精简,只包含了pay相关的方法,没有冗余的操作,符合接口隔离原则。但对于其他类的接口设计,也需要进一步检查是否存在类似 “胖接口” 的问题。
    耦合度:从类图看,模块之间的依赖关系比较清晰,没有出现复杂的循环依赖情况。不过像货物载重计算这种复杂的计算逻辑,可以进一步提取成独立的服务类,降低类与类之间因为复杂计算带来的耦合度,提高代码的可测试性和可维护性。
四、优化建议
  • 规模方面:赶紧补上核心逻辑的注释,让代码 “自解释”。同时,对于那些较长、功能复杂的方法,拆分成多个功能单一的小方法。
  • 质量方面:仔细梳理分支结构,简化深度大的嵌套。并且始终牢记每个方法只做一件事,保证方法的职责单一性。
  • 设计方面:充分利用抽象接口来解耦不同模块。对于新增功能,先思考是否符合开闭原则,不要出现硬编码的情况,提高代码的扩展性和灵活性。

测试通过截图

三、踩坑心得

类职责划分模糊导致的耦合问题

初期设计时,Order类同时承担订单信息存储、货物运费计算和支付流程处理,例如getTotalMoney()方法直接调用Goods的计费逻辑,违背单一职责原则。当需要新增货物类型时,不得不修改Order和Goods两个类,代码维护成本激增。

支付模块扩展性不足

支付方式通过if-else判断实现(如if(pay.equals("Wechat"))),未抽象为接口。新增支付方式时需修改Main类的调用逻辑,违反开闭原则。例如添加BankPay时,必须修改订单处理流程,导致系统扩展性差。

业务逻辑与代码实现的偏差

在Goods.getRate()方法中,费率计算的重量分段条件虽覆盖了业务需求,但未提取为独立策略类。例如普通货物费率的分段逻辑(<20kg、20-50kg)直接硬编码,后续若调整费率规则需修改多处代码,存在维护风险。

四、总结

设计原则的实践价值

通过接口抽象(如Payment)和职责拆分(Order类),系统扩展性显著提升。新增支付方式或货物类型时,只需扩展实现类,核心逻辑无需修改,验证了开闭原则的有效性。

代码质量的改进方向

目前Goods类仍混合了数据属性与计算逻辑(如getChargeableWeight()),可进一步拆分为GoodsEntity(存储属性)和GoodsCalculator(处理计算),强化单一职责。
注释率不足的问题需改善,例如在Flight.canCarry()方法中添加业务注释,说明载重校验规则,提升代码可读性。

面向对象思维的提升

本次实践深刻体会到 “组合优于继承” 的设计思想,如Order类通过组合Agent、Goods等对象实现功能,而非过度继承,降低了类间耦合。未来开发中需优先考虑接口抽象与模块解耦,避免 “大泥球” 式代码结构。

posted @ 2025-05-25 15:57  24201132-张如垚  阅读(38)  评论(0)    收藏  举报