• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
xiaoyu-2005
博客园    首页    新随笔    联系   管理    订阅  订阅

PTA题目集 8-9 总结性 Blog:航空货运管理系统的面向对象设计

一、前言

题目集 8-9 以 “航空货运管理系统” 为核心,聚焦面向对象设计原则(SOLID 原则)的综合应用,涵盖类设计、继承、多态、接口抽象、复杂业务逻辑实现等关键知识点。其中,题目集 8 要求基于基础类设计实现货运流程,重点考核单一职责、合成复用等原则;题目集 9 引入客户类型、货物类型的继承体系,新增折扣率、支付方式等扩展需求,强化里氏代换、开闭原则及依赖倒转原则的应用。

  1. 知识点覆盖
    类设计与职责划分:需设计客户(Customer)、货物(Cargo)、航班(Flight)、订单(Order)等核心类,确保单一职责。

继承与多态:题目集 9 中客户分为个人(IndividualCustomer)与集团(CorporateCustomer),货物分为普通(NormalCargo)、加急(ExpediteCargo)、危险(DangerousCargo),通过抽象类与子类实现多态。

接口与抽象类:定义抽象方法(如 Customer.getDiscountRate ()、Cargo.getRate ())规范子类行为。

复杂业务逻辑:计费重量计算(体积重量与实际重量取最大值)、分段费率计算、折扣应用、支付方式处理等。

异常处理与流程控制:航班载重超限判断、输入数据合法性校验(题目隐含要求)。

  1. 题量与难度分析
    题量:两次题目集均围绕同一系统迭代开发,题目集 8 为基础版本,题目集 9 为扩展版本,需在原有代码基础上新增客户 / 货物类型、支付方式等功能。

难度:
题目集 8:基础难度,重点在于类的合理拆分与职责分配,如 Cargo 类需封装计费重量与运费计算逻辑,Flight 类管理载重状态。

题目集 9:中等难度,引入继承体系后需确保里氏代换原则,如子类必须完全实现父类抽象方法;同时需处理组合关系(如 Order 包含多个 Cargo 实例),并扩展支付方式的策略模式(虽未显式使用接口,但支付方式的输出处理体现策略思想)。

二、设计与分析

(一)题目集 8:基础版本设计

  1. 类结构与职责分析
    类图(基于 PowerDesigner 绘制)

题目集8类图

Customer 类:存储客户基础信息(编号、姓名、电话、地址),单一职责。

Cargo 类:
核心方法:calculateChargeableWeight()计算计费重量(体积重量与实际重量取最大值),calculateRate()根据重量分段确定费率,calculateFreight()计算运费。
问题:费率计算逻辑硬编码在 Cargo 类中,若新增货物类型需修改此类,违背开闭原则。

Flight 类:管理航班载重(maxLoadCapacity、currentLoad),提供addLoad()更新载重,isOverloaded()判断超限。

Order 类:组合 Cargo 实例,计算订单总重量与总运费,依赖 Flight 完成载重校验。

  1. 复杂度分析(SourceMonitor 数据)

分析:
Cargo 类复杂度较高,因费率计算使用多层if-else分支(如重量分段判断),可通过策略模式优化(题目集 9 改进)。
Flight 类职责单一,复杂度低,符合设计原则。

  1. 核心逻辑实现
// Cargo类计费重量计算
private void calculateChargeableWeight() {
    double volumeWeight = (width * length * height) / 6000;
    chargeableWeight = Math.max(actualWeight, volumeWeight);
}
// Order类总运费计算
public void addCargo(Cargo cargo) {
    totalWeight += cargo.getChargeableWeight();
    totalFreight += cargo.getFreight();
}

(二)题目集 9:扩展版本设计(继承与多态应用)

  1. 类结构演进
    类图(基于 PowerDesigner 绘制)

题目集9类图

客户体系:
Customer抽象类:定义getDiscountRate()抽象方法(个人客户 0.9 折,集团客户 0.8 折)。

IndividualCustomer/CorporateCustomer子类:实现具体折扣逻辑。

货物体系:
Cargo抽象类:定义getRate()抽象方法,具体费率由子类实现(普通 / 加急 / 危险货物费率不同)。

NormalCargo/ExpediteCargo/DangerousCargo子类:重写费率计算逻辑。

支付方式:通过字符串标识(Wechat/ALiPay/Cash),输出时映射为中文,隐含策略模式思想(后续可扩展为接口)。

  1. 复杂度分析(SourceMonitor 数据)

改进点:

货物费率计算逻辑移至子类,符合开闭原则(新增货物类型只需新建子类,无需修改原有代码)。
客户折扣通过继承实现,子类可无缝替换父类,符合里氏代换原则。

  1. 多态与继承实现
// 抽象类Customer
public abstract class Customer {
    protected String type;
    public abstract double getDiscountRate();
}
// 子类CorporateCustomer
public class CorporateCustomer extends Customer {
    public CorporateCustomer(...) {
        super("Corporate", ...);
    }
    @Override 
public double getDiscountRate() { return 0.8; }
}
// 多态调用(Order类计算总费用)
double totalFee = cargos.stream()
    .mapToDouble(c -> c.getChargeableWeight() * c.getRate())
    .sum() * customer.getDiscountRate();

三、采坑心得

  1. 继承体系设计缺陷(题目集 9)

问题:初始设计中Cargo子类直接复用题目集 8 的费率分段逻辑,导致代码冗余(如NormalCargo.getRate()与题目集 8 的Cargo.calculateRate()逻辑重复)。

解决方案:提取公共逻辑到抽象类,通过模板方法模式优化。

// 抽象类Cargo新增模板方法
protected double getBaseRate(double weight) {
    if (weight < 20) return 35;
    else if (weight < 50) return 30;
    // 其他分段...
}
// 子类ExpediteCargo调用模板方法
@Override 
public double getRate() {
    double weight = getChargeableWeight();
    return getBaseRate(weight) * 1.5; // 加急货物费率上浮50%
}
  1. 线程安全与数据一致性(隐含需求)

场景:多订单并发处理时,Flight 的currentLoad可能出现竞态条件(如多个订单同时修改航班载重)。

解决方案:引入synchronized关键字或AtomicDouble保证原子性(题目虽未明确多线程,但代码需具备可扩展性)。

private final AtomicDouble currentLoad = new AtomicDouble(0);
public void addLoad(double weight) {
    currentLoad.addAndGet(weight);
}
  1. 测试用例设计不足

问题:题目集 9 中危险货物费率计算错误(如重量≥100 时费率应为 20,误写为 15),因测试用例未覆盖边界值。

测试数据补充:

货物类型 重量 (kg) 体积 (cm³) 预期计费重量 预期费率
危险货物 100 600000 100 20
加急货物 19 100000 16.666... 60

  1. 代码复杂度过大(题目集 8)

现象:Cargo类的calculateRate()方法v(G)= 7,违反复杂度阈值(通常建议≤5)。

优化:将费率分段逻辑封装为独立方法或枚举类。

// 枚举类RateStrategy
enum RateStrategy {
    LESS_THAN_20(35), BETWEEN_20_50(30), /* 其他分段 */;
    private final double rate;
    RateStrategy(double rate) { this.rate = rate; }
    public double getRate(double weight) { /* 返回对应费率 */ }
}

四、改进建议

  1. 设计模式应用

策略模式:将货物费率计算、客户折扣、支付方式处理均抽象为策略接口,消除if-else分支。

// 费率策略接口
interface RateStrategy {
    double calculateRate(double weight);
}
class NormalRateStrategy implements RateStrategy { /* 实现逻辑 */ }

工厂模式:通过工厂类创建客户、货物实例,隔离客户端与具体子类。

class CustomerFactory {
    public static Customer createCustomer(String type, ...) {
        if ("Individual".equals(type)) return new IndividualCustomer(...);
        // 其他类型...
    }
}
  1. 代码结构优化

单一职责强化:将支付方式处理从 Order 类中拆分,独立为 PaymentService 类。

接口隔离:定义Payable接口,实现类(WechatPay、AliPay)封装支付逻辑。

interface Payable {
    String getPaymentText();
    double calculateFinalFee(double originalFee); // 如微信支付可能有手续费
}
  1. 异常处理增强

输入校验:在客户、货物、航班信息输入时,增加格式校验(如航班日期格式、货物尺寸合法性)。

自定义异常:抛出LoadExceededException、InvalidCargoException等,提升错误定位效率。

  1. 性能优化
    缓存机制:对重复计算的计费重量、费率结果进行缓存(如同一货物多次计算时)。

集合选择:使用LinkedList存储订单货物,提升频繁插入 / 删除场景的性能(题目中为顺序添加,当前使用 ArrayList 已足够)。

五、总结

  1. 核心收获

SOLID 原则实践:
单一职责(SRP):确保每个类专注于单一功能(如 Flight 仅管理载重,Order 仅处理订单逻辑)。

里氏代换(LSP):子类可完全替代父类,如CorporateCustomer实例可直接用于需要Customer的场景。

开闭原则(OCP):题目集 9 新增货物类型时,通过新建子类实现,未修改原有代码。

多态与继承:通过抽象类与接口规范行为,子类实现具体逻辑,代码扩展性显著提升。

测试驱动开发(TDD):通过边界值测试(如货物重量 = 20、50、100)发现隐藏逻辑错误,确保代码鲁棒性。

  1. 待提升方向

设计模式深度应用:未在题目中使用工厂模式、观察者模式等,后续需在复杂场景中尝试。

代码复杂度控制:部分方法仍存在多层分支,需通过重构(如策略模式、状态模式)降低复杂度。

单元测试完善:目前测试集中于核心逻辑,缺少对异常场景(如空订单、非法输入)的覆盖。

  1. 对课程的建议

延续题目集 8-9 的迭代模式,在同一系统中逐步增加需求(如多航班调度、国际货运关税计算),强化系统设计的持续性。

六、结语

题目集 8-9 通过 “航空货运管理系统” 的迭代开发,系统性考察了面向对象设计的核心原则与实践能力。从基础类设计到继承体系构建,再到复杂业务逻辑的多态实现,过程中虽面临代码复杂度控制、测试覆盖等挑战,但通过合理的类职责划分、设计模式应用及持续重构,最终实现了可扩展、易维护的系统架构。未来需进一步将设计原则融入日常编码习惯,提升代码的健壮性与可复用性,为大型软件项目开发奠定坚实基础。

posted @ 2025-05-23 10:02  NCHU_Wireless  阅读(27)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3