航空货运管理系统分析

前言:

  本次Blog随笔是针对PTA题目集8、9进行分析的,主要针对题目集8、9的最后一题航空货运管理系统来进行分析。题目集8的前两题分别是点线面问题重构和雨刷程序功能扩展设计,这两个问题所考查的是多态,同时点线面问题还考查了继成,通过使用抽象类,实现不同类的继承。题目集9前两题考查魔方问题和点线面问题的迭代问题,魔方问题同题目集8第一题一样考查多态和继承,使用抽象类来进行继承,点线面问题的迭代问题是在题目集8的点线面问题的基础上考查泛型(泛型是编程语言中一种允许在定义类、接口或方法时使用类型参数的特性)和ArrayList。以上是对题目集8、9前两个问题的简单总结,接下来我将会针对题目集8、9的最后一个问题——航空货运管理系统,针对其涉及的类设计还有继承的使用来进行分析。

设计与分析:

  题目集8的航空运输管理系统:

某航空公司“航空货运管理系统”中的空运费的计算涉及多个因素,通常包括货物重量/体积、运输距离、附加费用、货物类型、客户类型以及市场供需等。

本次作业主要考虑货物重量/体积,以下是具体的计算方式和关键要点:

一、计费重量的确定

空运以实际重量(Gross Weight)和体积重量(Volume Weight)中的高者作为计费重量。

计算公式:

体积重量(kg= 货物体积(长××高,单位:厘米)÷ 6000

示例:

若货物实际重量为 80kg,体积为 120cm×80cm×60cm,则:

体积重量 = (120×80×60) ÷ 6000 = 96kg

计费重量取 96kg(因 96kg > 80kg)。

二、基础运费计算

费率(Rate:航空公司或货代根据航线、货物类型、市场行情等制定(如CNY 30/kg)。本次作业费率采用分段计算方式:

公式:基础运费 = 计费重量 × 费率

三、题目说明

本次题目模拟某客户到该航空公司办理一次货运业务的过程:

航空公司提供如下信息:

航班信息(航班号,航班起飞机场所在城市,航班降落机场所在城市,航班日期,航班最大载重量)

客户填写货运订单并进行支付,需要提供如下信息:

·客户信息(姓名,电话号码等)

·货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)

·运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选航班号,订单日期)

· 支付方式(支付宝支付、微信支付)

注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独计费。

程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信 息报表及货物明细报表。

四、题目要求

本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开闭原则以及合成复用原则,除需要在 PTA 平台提交源码外,还需要在超星平台

提交本次作业最终得分源码(首次提交最高分源码)的类图,评判标准为:

基础得分:PTA 实际得分

设计因素:单一职责原则(40%)、里氏代换原则(20%)、开闭原则(20%)、 合成复用原则(20%最终得分:基础得分扣减所有违背设计原则分值(违背所有四个原则的设计 最终得分为 0 分)

 算法分析:

1. 重量计算算法(Things类)

double volumeWeight = wide * height * length / 6000.0;
this.Weight = Math.max(volumeWeight, weight);

该算法执行两个关键操作:

  1. 计算体积重量:将货物的三维尺寸相乘后除以6000(标准航空体积重量系数)
  2. 取实际重量和体积重量的较大值作为最终计费重量

2. 总重量计算算法(ShippingCalculator类)

for (int i = 0; i < things.size(); i++) {
    Things t = things.get(i);
    totalWeight += t.Weight;
}

算法流程:

  1. 初始化totalWeight为0
  2. 遍历所有货物,累加每个货物的Weight属性(即之前计算的计费重量)
  3. 返回累加结果

3. 运费计算算法(ShippingCalculator类)

for (int i = 0; i < n; i++) {
    double weight = things.get(i).Weight;
    if (weight < 20) {
        result.rates[i] = 35;
    } else if (weight < 50) {
        result.rates[i] = 30;
    } else if (weight < 100) {
        result.rates[i] = 25;
    } else {
        result.rates[i] = 15;
    }
    result.fees[i] = weight * result.rates[i];
    result.totalMoney += result.fees[i];
}

算法采用分层费率结构:

  1. <20kg:费率35元/kg
  2. 20-50kg:费率30元/kg
  3. 50-100kg:费率25元/kg
  4. ≥100kg:费率15元/kg

 心得体会:

   在此次题目中的编写中我认为它最能体现的思维是封装思维,在程序面对较多对象时,封装思维大大提高了代码的可维护性和复用性;与此同时封装性使对象的行为更加清晰明了,有利于实现复杂的业务规则,增加代码的可读性。

  题目集9的航空运输管理系统:

某航空公司“航空货运管理系统”中的空运费的计算涉及多个因素,通常包括货物重量/体积、运输距离、附加费用、货物类型、客户类型以及市场供需等。

本次作业主要考虑货物重量/体积,以下是具体的计算方式和关键要点:

一、计费重量的确定

空运以实际重量(Gross Weight)和体积重量(Volume Weight)中的

高者作为计费重量。

计算公式:

体积重量(kg= 货物体积(长××高,单位:厘米)÷ 6000

示例:

若货物实际重量为 80kg,体积为 120cm×80cm×60cm,则:

体积重量 = (120×80×60) ÷ 6000 = 96kg

计费重量取 96kg(因 96kg > 80kg)。

二、基础运费计算

1费率(Rate:航空公司或货代根据航线、货物类型、市场行情等制定(如CNY 30/kg)。本次作业费率与货物类型有关,货物类型分为普通货物、危险货物和加急货物三种,其费率分别为:

计算公式基础运费 = 计费重量 × 费率 × 折扣率

其中,折扣率是指不同的用户类型针对每个订单的运费可以享受相应的折扣,

在本题中,用户分为个人用户和集团用户,其中个人用户可享受订单运费的 9折优惠,集团用户可享受订单运费的 8 折优惠。

三、题目说明

本次题目模拟某客户到该航空公司办理一次货运业务的过程:

航空公司提供如下信息:

航班信息(航班号,航班起飞机场,航班降落机场,航班日期,航班最大载重量)

2客户填写货运订单并进行支付,需要提供如下信息:

·客户信息(姓名,电话号码等)

·货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)

·运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选航班号,订单日期)

·支付方式(支付宝支付、微信支付、现金支付)

注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独计费。

程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信 息报表及货物明细报表。

四、题目要求

本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开 闭原则以及合成复用原则、依赖倒转原则除需要在 PTA 平台提交源码外,还 需要在超星平台提交本次作业最终得分源码(首次提交最高分源码)的类图

评判标准为:

基础得分:PTA 实际得分

设计因素:单一职责原则(20%)、里氏代换原则(20%)、开闭原则(20%)、 合成复用原则(20%)、依赖倒转原则(20%)。

最终得分:基础得分扣减所有违背设计原则分值(违背所有五个原则的设计 最终得分为 0 分)

 

 算法分析:

  

1. 重量计算算法(Things类)

double volumeWeight = wide * height * length / 6000.0;
this.Weight = Math.max(volumeWeight, weight);

该算法执行两个关键操作:

  1. 计算体积重量:将货物的三维尺寸相乘后除以6000(标准航空体积重量系数)
  2. 取实际重量和体积重量的较大值作为最终计费重量

2. 总重量计算算法(ShippingCalculator类)

for (int i = 0; i < things.size(); i++) {
    Things t = things.get(i);
    totalWeight += t.Weight;
}

算法流程:

  1. 初始化totalWeight为0
  2. 遍历所有货物,累加每个货物的Weight属性(即之前计算的计费重量)
  3. 返回累加结果

3. 运费计算算法(ShippingCalculator类)

普通货物计算逻辑:

if(kind.equals("Normal")){
    for (int i = 0; i < n; i++) {
        double weight = things.get(i).Weight;
        if (weight < 20) {
            result.rates[i] = 35;
        } else if (weight < 50) {
            // ...
        }
        if(Ukind.equals("Corporate"))
            result.fees[i] = weight * result.rates[i]*0.8;
        // ...
    }
}

算法分为三个主要分支:

  1. 普通货物​:

    • <20kg:费率35元/kg
    • 20-50kg:费率30元/kg
    • 50-100kg:费率25元/kg
    • ≥100kg:费率15元/kg
  2. 加急货物​:

    • <20kg:费率60元/kg
    • 20-50kg:费率50元/kg
    • 50-100kg:费率40元/kg
    • ≥100kg:费率30元/kg
  3. 危险品​:

    • <20kg:费率80元/kg
    • 20-50kg:费率50元/kg
    • 50-100kg:费率30元/kg
    • ≥100kg:费率20元/kg

最后根据用户类型应用折扣:

  • 企业用户(Corporate):8折
  • 个人用户(Individual):9折

4. 输出格式化算法

System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n", i+1, things.get(i).name, things.get(i).Weight,result.rates[i], result.fees[i]/0.8);

输出时:

  1. 对每件货物生成明细行
  2. 根据用户类型反向计算显示原价(企业用户除以0.8,个人用户除以0.9)
  3. 统一保留1位小数格式

 心得体会:

  此次代码在第二次代码的基础上增加了不同物品计算费率不同,不同顾客折扣不同,以及不同的支付方式输出不同的要求。本次代码为实现这些功能采用的时字符串比较的方法,通过equals(不可以用“=”,否则判断的是字符串的地址)来判断输入的内容符合哪个条件,来完成要求。但这样使代码并不灵活,降低了代码的可维护性,我们可以发现真假的要求背后显示出来的是对象不同但行为相同的现象,因此我们可以用继承的方法来实现要求,增强代码的可维护性。

 踩坑心得:

  本次采坑主要在于在做题时并没有注意到对象行为的一致性,没有这个察觉从而没想到对于继承的运用,这也是代码较为冗杂,降低了代码的可维护性和可读性。

 改进建议:

通过分析发现,不同对象(如不同物品、顾客、支付方式)的行为本质相同​(例如计算费率、应用折扣、生成支付输出),但具体实现不同。这种场景适合用继承和多态重构:

  1. 封装共性行为​:将共用逻辑抽象到父类中(如ItemCustomerPaymentMethod)。
  2. 子类差异化实现​:通过子类覆盖父类方法实现特定规则(如ElectronicsItem重写费率计算逻辑)。
  3. 工厂模式管理对象​:通过工厂类根据输入参数动态创建对应的子类对象,避免手动new和条件分支。

重构示例

1. 物品类型的继承实现
abstract class Item {
    protected String name;
    protected double price;

    public Item(String name, double price) {
        this.name = name;
        this.price = price;
    }

    // 抽象方法:由子类实现具体费率计算
    public abstract double calculateFee();
}

class ElectronicsItem extends Item {
    public ElectronicsItem(String name, double price) {
        super(name, price);
    }

    public double calculateFee() {
        return price * 0.1; // 电子产品费率10%
    }
}

class GroceryItem extends Item {
    public GroceryItem(String name, double price) {
        super(name, price);
    }

    public double calculateFee() {
        return price * 0.05; // 日用品费率5%
    }
}
2. 工厂模式创建对象
class ItemFactory {
    public static Item createItem(String type, String name, double price) {
        switch (type.toLowerCase()) {
            case "electronics":
                return new ElectronicsItem(name, price);
            case "grocery":
                return new GroceryItem(name, price);
            default:
                throw new IllegalArgumentException("未知物品类型: " + type);
        }
    }
}
改进后的优势
  1. 高内聚低耦合​:每种类型的逻辑封装在各自的类中,修改时互不影响。
  2. 易于扩展​:新增类型只需添加子类和更新工厂,无需修改现有业务逻辑。
  3. 代码可读性提升​:业务逻辑通过对象行为表达,而非冗长的条件判断。

总结:

在初期版本中,封装思维确实有效组织了代码结构,将重量计算、运费规则等逻辑隔离在特定类中,使业务逻辑清晰可见。但随着需求扩展(如不同货物类型、用户折扣、支付方式),单纯依赖字符串比较和条件分支(equals+if-else)暴露了局限性——虽然功能实现了,但代码臃肿且难以维护。

此时,​多态和策略模式的价值凸显:

  1. 费率计算​:可定义ShippingStrategy接口,派生出NormalStrategyExpediteStrategy等子类,通过对象类型而非字符串决定行为
  2. 折扣规则​:采用DiscountPolicy接口,CorporatePolicyIndividualPolicy分别实现折扣逻辑
  3. 支付方式​:通过PaymentMethod接口统一输出模板,微信/支付宝实现各自格式化逻辑

这种改进将"是什么"(字符串判断)转化为"能做什么"(接口方法),符合开闭原则——新增类型只需扩展类而非修改原有条件分支。

封装构建了可靠的基础设施,而多态和策略模式赋予了应对变化的弹性。在业务规则复杂的系统中,应当:

  1. 优先用对象替代条件判断
  2. 通过接口定义行为契约
  3. 让类的层次结构反映业务差异

这种思维不仅能提升当前代码质量,更是应对未来需求迭代的未雨绸缪。

posted @ 2025-05-19 19:53  邵星瑶  阅读(40)  评论(0)    收藏  举报