航空货运管理系统设计与实现:从三次电梯题到航空货运的进阶之路

一、前言

1. 初次接触:从电梯调度到货运系统的思维转变

最初面对电梯题目时,因复杂的状态转移逻辑而倍感吃力,熬夜尝试编写代码却因逻辑漏洞陷入死循环。这种挫败感在接触航空货运管理系统时再次浮现——输入项的多层嵌套(如货物明细的循环输入)、类之间的依赖关系(客户、货物、航班、订单的关联),如同电梯调度中的方向与楼层判断般错综复杂。
关键突破:借鉴电梯题中“队列管理请求”的思路,将客户信息、货物明细、航班数据分别封装为独立类,通过订单类整合各要素,避免数据混杂。

2. 二次挑战:代码结构的优化与重构

第二次电梯题中,尝试用“锁定目标楼层法”整合调度逻辑,但深陷嵌套if-else的泥沼。类似地,在货运系统中,计费逻辑(不同货物类型的费率策略、客户折扣)成为新的“逻辑黑洞”。
改进实践

  • 将计费规则抽象为RateStrategy接口,通过DefaultRateStrategy实现具体策略,避免在订单处理中硬编码费率条件。
  • 参考电梯题的队列设计,用ArrayList管理货物明细,通过Order.addCargo()方法解耦数据添加与订单逻辑。

3. 最终实现:从功能完成到健壮性提升

三次电梯题通过每日主动思考代码逻辑,最终实现完整运行。这种坚持迁移到货运系统中,使我可以在重复报错的问题中坚持下来。在我坚持下来之后 ,面向对象设计原则中的单一职责原则、里氏代换原则、开闭原则以及合成复用原则、依赖倒转原则更是另外的大山,压倒我的稻草。通过查找课本以及学堂在线上面的网课,是我逐渐明白了一些。

4.我的思路

代码先通过区分企业和个人客户(企业8折、个人9折),用接口定义支付方式(微信/支付宝/现金)、货物类型(普通/加急/危险品)等不同策略。主程序读取输入数据创建客户、货物、航班、订单等对象,货物按“体积和实际重量取大值”算计费重量,再根据货物类型和重量查阶梯费率算运费,乘客户折扣得总费用。同时检查订单总重量是否超过航班剩余容量,没超就扣减容量并输出订单详情(客户、航班、收发件人、货物明细、支付方式及金额),超了就提示超载。核心是用面向对象和策略模式让不同功能模块独立,方便后续扩展新客户类型或计费规则。

二、系统设计与核心逻辑分析

1. 类结构设计:分层与职责分离

classDiagram Customer <|-- CorporateCustomer Customer <|-- IndividualCustomer PaymentMethod <|-- WechatPay PaymentMethod <|-- Alipay PaymentMethod <|-- Cash CargoType <|-- NormalCargo CargoType <|-- ExpediteCargo CargoType <|-- DangerousCargo WeightCalculator <|-- NormalWeightCalculator Flight --> Order Order --> Customer Order --> Flight Order --> PaymentMethod Order "1" --> "*" Cargo RateStrategy --> OrderProcessor

2. 核心类解析

(1)客户体系:Customer及其子类

  • 抽象类Customer:封装客户基础信息(编号、姓名、电话、地址),通过getDiscount()预留折扣策略扩展点。
  • 企业客户CorporateCustomer:享受8折优惠(getDiscount()=0.8)。
  • 个人客户IndividualCustomer:享受9折优惠(getDiscount()=0.9)。

(2)货物与计费:Cargo与策略模式

  • Cargo
    • 包含实物属性(长宽高、重量)与计费属性(计费重量chargeWeight)。
    • 通过WeightCalculator接口计算计费重量(体积重量与实际重量取较大值)。
  • RateStrategy接口:根据货物类型(CargoType)和重量动态获取费率,例如:
    • 加急货物(Expedite)重量<20kg时费率为60元/kg,20-50kg时为50元/kg。

(3)航班与订单:FlightOrder的协同

  • Flight:管理航班的载重量(maxCapacity)与剩余容量(remainingCapacity),通过subtractCapacity()扣除已承载重量。
  • Order:作为数据枢纽,整合客户、航班、货物、支付方式,通过OrderProcessor完成计费与校验。

3. 关键流程:订单处理逻辑

  1. 输入解析:按顺序读取客户、货物、航班、订单信息,通过工厂模式创建具体类实例(如根据客户类型创建CorporateCustomerIndividualCustomer)。
  2. 容量校验:计算订单总计费重量,若超过航班剩余容量,输出错误信息并终止程序。
  3. 费用计算
    • 总费用 = (货物计费重量×对应费率)之和 × 客户折扣。
    • 示例:企业客户托运加急货物,总费用享受8折优惠。
  4. 输出格式化:按指定格式输出订单信息与货物明细,保留1位小数。

4.核心算法设计:

在运费计算方面,根据题目要求,需要综合考虑货物的计费重量、货物类型、客户类型折扣以及支付方式等因素。具体算法如下:
对于每件货物,先计算其体积重量(长 × 宽 × 高 ÷ 6000),然后与实际重量比较,取较大者作为计费重量。
根据货物类型和计费重量,通过 RateStrategy 获取相应的费率。
将每件货物的计费重量乘以费率,得到该货物的运费,累加所有货物的运费得到运费小计。
根据客户类型获取折扣率,运费小计乘以折扣率得到最终的支付金额。
在订单处理流程中,先检查订单中所有货物的总重量是否超过航班剩余载重量,如果超过则输出相应提示信息并终止程序;否则,输出完整的订单信息和货物明细。

5.剖析代码及底层逻辑

主要设计思路
整个代码的设计思路围绕面向对象的 principles 展开,通过合理的类划分和接口定义,实现了各模块之间的低耦合和高内聚,使得系统具有良好的可扩展性和可维护性。以订单处理为例,OrderProcessor 类依赖于 RateStrategy 接口来计算运费,而具体的运费计算策略由 DefaultRateStrategy 实现,这样的设计遵循了依赖倒转原则,便于在后续扩展新的运费计算策略时,只需新增实现类而无需修改原有代码。
关键代码分析
在 Customer 抽象类及其子类中:

abstract class Customer {
    // 属性定义
    public Customer(String id, String name, String phone, String address) {
        // 构造函数初始化属性
    }
    // getter 方法
    public abstract String getType();
    public abstract double getDiscount();
}

class CorporateCustomer extends Customer {
    public CorporateCustomer(String id, String name, String phone, String address) {
        super(id, name, phone, address);
    }
    @Override
    public String getType() { return "Corporate"; }
    @Override
    public double getDiscount() { return 0.8; }
}

class IndividualCustomer extends Customer {
    public IndividualCustomer(String id, String name, String phone, String address) {
        super(id, name, phone, address);
    }
    @Override
    public String getType() { return "Individual"; }
    @Override
    public double getDiscount() { return 0.9; }
}

通过抽象类定义客户的基本属性和共性方法,并由子类实现具体的客户类型和折扣策略,体现了里氏代换原则,使得在使用客户对象时,可以统一处理不同类型的客户。
在 Cargo 类中:

class Cargo {
    // 属性定义
    public Cargo(
            String id, String name,
            double width, double length, double height, double weight,
            WeightCalculator calculator, CargoType type) {
        this.id = id;
        this.name = name;
        this.width = width;
        this.length = length;
        this.height = height;
        this.weight = weight;
        this.calculator = calculator;
        this.type = type;
        calculateChargeWeight();
    }
    private void calculateChargeWeight() {
        this.chargeWeight = calculator.calculateChargeWeight(width, length, height, weight);
    }
    // getter 方法
}

将货物的计费重量计算逻辑通过 WeightCalculator 接口注入到 Cargo 类中,实现了策略模式,使得计费重量的计算方式具有灵活性和可扩展性。
在 OrderProcessor 类的 processOrder 方法中:

public void processOrder(Order order) {
    double totalWeight = 0;
    double subtotal = 0;
    for (Cargo cargo : order.getCargoes()) {
        double chargeWeight = cargo.getChargeWeight();
        double rate = rateStrategy.getRate(chargeWeight, cargo.getType());
        subtotal += chargeWeight * rate;
        totalWeight += chargeWeight;
    }
    double totalPayment = subtotal * order.getCustomer().getDiscount();
    Flight flight = order.getFlight();
    if (totalWeight > flight.getRemainingCapacity()) {
        System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.%n",
                flight.getNumber());
    } else {
        flight.subtractCapacity(totalWeight);
        // 输出订单信息和货物明细
    }
}

集中处理了订单的业务逻辑,包括运费计算、订单信息输出等,遵循了单一职责原则,使得该类的职责清晰明确。

代码检测结果:

三、踩坑记录与优化方向

1. 已解决的典型问题

  • 问题1:货物数量与输入循环次数不匹配
    解决方案:通过cargoCount变量控制循环次数,确保每个货物明细正确读取。
  • 问题2:支付方式字符串匹配不严谨
    解决方案:使用switch-case替代多重if-else,并添加默认值处理非法输入。

2. 待优化点

优化方向 具体措施
代码健壮性 - 将货物类型、支付方式改为枚举类,避免字符串硬编码。
- 添加输入合法性校验(如楼层范围、日期格式)。
代码可读性 - 拆分DefaultRateStrategy中的长条件语句为独立方法。
- 为关键变量添加有意义的命名(如chargeWeight改为chargeableWeight)。
性能优化 - 使用数组替代ArrayList存储少量货物(若已知最大数量),提升访问效率。
设计模式 - 引入工厂模式创建CargoTypePaymentMethod实例,减少main方法中的switch-case

四、学习收获与未来展望

1. 能力提升

  • 面向对象设计:从电梯题的“过程式编程”转向“类-接口-策略”的分层设计,理解抽象类与接口的适用场景。
  • 问题拆解:将复杂业务(如货运计费)拆解为“重量计算→费率获取→折扣应用”的模块化流程,降低调试难度。

2. 未来探索

  • 扩展功能
    • 添加历史订单查询、航班状态跟踪等模块。
    • 实现多航班调度(如同一订单拆分至多个航班)。
  • 技术升级
    • 引入Java泛型优化集合类型(如List<Cargo>明确元素类型)。
    • 使用日志框架(如Log4j)记录操作日志,替代System.out.println

五、代码附录:核心逻辑片段

// 订单处理核心逻辑
public void processOrder(Order order) {
    double totalWeight = 0;
    double subtotal = 0;
    
    for (Cargo cargo : order.getCargoes()) {
        double chargeWeight = cargo.getChargeWeight();
        double rate = rateStrategy.getRate(chargeWeight, cargo.getType());
        subtotal += chargeWeight * rate;
        totalWeight += chargeWeight;
    }
    
    double totalPayment = subtotal * order.getCustomer().getDiscount();
    
    Flight flight = order.getFlight();
    if (totalWeight > flight.getRemainingCapacity()) {
        // 容量不足处理
    } else {
        // 正常处理逻辑,输出订单信息
    }
}

总结

从电梯调度到航空货运,两次作业的挑战本质上都是“复杂状态下的逻辑建模”。通过封装变化点(如电梯的方向策略、货运的费率策略)、分离职责(将输入解析、业务逻辑、输出格式化拆分到不同类),逐步构建出可维护的系统架构。而我缺少面向对象设计原则中的单一职责原则、里氏代换原则、开闭原则以及合成复用原则、依赖倒转原则的理解,导致编写代码不够规范。未来我仍需进一步强化设计模式的应用,代码编写原则的详细学习,让代码不仅能“跑通”,更能“优雅地生长”。

posted on 2025-05-19 15:18  蝶野  阅读(55)  评论(0)    收藏  举报