第二次Blog作业

前言
在当今数字化的业务环境中,高效、准确地处理订单信息至关重要。这里呈现的两段 Java 代码围绕航班货物运输订单管理展开。它们旨在整合客户信息、货物详情、航班数据以及订单相关操作,以实现对订单全流程的管理与计算。

第一段代码初步构建了一个基础的订单处理框架,涵盖了客户、货物、航班和订单类的设计。通过定义不同类的属性和方法,初步实现了订单信息的录入、处理和输出。然而,在代码结构和设计原则的遵循上存在一定不足,这为后续的改进提供了方向。

第二段代码在第一段的基础上进行了拓展和优化。引入了枚举类型来规范客户类型、货物类型和支付方式等,增强了代码的可读性和可维护性。同时,运用了抽象类和继承机制,使不同客户和货物类型的差异化处理更加清晰。但代码在复杂度控制、输入校验和异常处理等方面仍有待完善。

这两段代码展示了从基础实现到逐步优化的过程,对于理解订单管理系统的开发以及面向对象编程在实际场景中的应用具有重要的参考价值。我们将从代码结构、设计模式应用、复杂度分析等多个角度对其进行深入剖析,探讨其优点与不足,并提出针对性的改进建议。

题目集8航空货运管理系统
一、设计与分析

  • 1. 类设计与单一职责原则
    Customer类:负责存储客户信息(编号、姓名、电话、地址),职责单一,符合单一职责原则。

Goods类:包含货物基础信息(编号、名称、尺寸、重量)和计算逻辑(体积重量、计费重量、费率、运费)。
潜在问题:计算逻辑与数据存储耦合,可拆分为独立的GoodsCalculator工具类,使Goods仅负责数据存储,更符合单一职责原则。

Flight类:存储航班信息(航班号、起降机场、日期、最大载重量),职责明确,符合原则。

Order类:处理订单信息(订单号、日期、收发件人信息)、关联客户 / 航班 / 货物,并包含计算总重量、总费用和打印输出逻辑。
潜在问题:打印逻辑与业务逻辑混合(如printOrderInfo方法),可拆分为独立的OrderPrinter类,避免职责过重。

  • 2. 里氏代换原则
    代码中未使用继承关系,因此不涉及里氏代换原则的直接应用。
    建议:若后续扩展(如区分 “普通货物” 和 “特殊货物”),需通过继承实现时,需确保子类可完全替代父类,不改变父类方法的前置 / 后置条件。
  • 3. 开闭原则
    费率计算:当前Goods.getRate()方法通过条件判断实现分段费率,若新增费率区间需修改该方法,违背开闭原则。
    改进方向:使用策略模式,将费率计算封装为策略接口(如RateStrategy),不同重量区间对应不同策略类,新增策略时无需修改原有代码。
    支付方式:代码中硬编码 “微信支付”,若新增支付方式(如支付宝),需修改Order类,违背开闭原则。
    改进方向:定义支付接口(Payment),不同支付方式实现接口,通过依赖注入动态切换。
  • 4. 合成复用原则
    代码通过组合(Order包含Customer、Flight、Goods对象)实现复用,而非继承,符合合成复用原则。
    优点:解耦类之间的关系,便于独立维护和扩展。
  • 5.深度分析



    代码复杂度分析

最高复杂度方法: 最复杂的方法是Goods.getRate() ,其复杂度值为 5 ,所在行号为 81 ,最大块深度为 3 。该方法中可能存在较多条件判断(如不同重量区间判断),导致复杂度上升。后续可考虑使用策略模式简化逻辑,降低复杂度。
平均复杂度: 平均复杂度为 1.17 ,平均块深度为 1.66 ,说明大部分方法逻辑相对简单,代码结构总体较为清晰。但仍有优化空间,如进一步拆分复杂方法。
方法复杂度详情
大部分方法复杂度为 1 ,如Customer和Flight类中的诸多获取属性方法,这些方法逻辑简单,仅返回对应属性值。
Goods类中部分方法复杂度稍高,除getRate()外,如calculateFreight() ,其复杂度为 2 ,因为涉及计费重量、费率和折扣率的计算逻辑。
分支语句情况
分支语句占比为 5.1% ,方法调用语句占比 24% 。分支语句占比不高,说明代码中条件判断逻辑不算特别多,但仍需关注Goods.getRate()等方法中分支逻辑的优化。
二、踩坑心得

  • 输入处理陷阱:
    在读取字符串前未消耗换行符(如scanner.nextLine()前未调用scanner.nextInt()后的scanner.nextLine()),可能导致输入错位。
    示例:输入goodsName时,若前一个输入是nextInt(),需先用scanner.nextLine()消耗剩余换行符,避免读取空字符串。
  • 浮点数精度问题:
    计算体积重量、运费时使用double类型,可能存在精度误差(如除运算)。
  • 建议:
    涉及货币计算时,优先使用BigDecimal保证精度,但需注意代码复杂度增加。
  • 类职责边界模糊:
    Order类同时处理业务逻辑和输出逻辑,导致代码冗余且不易维护。
  • 教训: 分离 “数据处理” 和 “界面展示”,遵循单一职责原则。
  • 异常处理缺失:
    未处理非法输入(如负数重量、非日期格式),可能导致程序崩溃。
  • 改进:
    添加输入校验逻辑,如货物尺寸、重量必须为正数,航班日期格式合法等。

三、改进建议

  • 1. 重构类职责(单一职责)
    将Goods中的计算逻辑拆分为GoodsCalculator工具类:
    public class GoodsCalculator {
    public static double calculateVolumeWeight(double width, double length, double height) {
    return (width * length * height) / 6000;
    }
    // 其他计算方法(费率、运费)同理
    }
    将Order中的打印逻辑拆分为OrderPrinter类:
    public class OrderPrinter {
    public static void printOrder(Order order) {
    // 统一处理输出格式,避免分散在业务类中
    }
    }
  • 2. 应用设计模式(开闭原则)
    策略模式重构费率计算:
    interface RateStrategy {
    double getRate(double weight);
    }
    class LessThan20Strategy implements RateStrategy {
    public double getRate(double weight) { return 35; }
    }
    // 其他重量区间策略类...

在Goods中通过策略接口动态获取费率,新增区间时只需实现新策略类。
工厂模式创建货物对象:
class GoodsFactory {
public static Goods createGoods(int id, String name, double... params) {
// 根据货物类型(普通/特殊)创建不同实例,隔离变化
}
}

  • 3. 增强输入校验
    在各实体类构造方法中添加参数校验:

public Goods(int goodsId, String goodsName, double width, double length, double height, double weight) {
if (width <= 0 || length <= 0 || height <= 0 || weight <= 0) {
throw new IllegalArgumentException("尺寸或重量不能为负数或零");
}
// ...其他初始化逻辑
}

  • 4. 优化输出格式
    使用String.format统一格式化输出,避免重复代码:

// 在OrderPrinter中定义通用格式
private static final String ORDER_INFO_FORMAT =
"客户:%s(%s)订单信息如下:\n" +
"-----------------------------------------\n" +
"航班号:%s\n" +
"订单号:%d\n" +
// ...其他字段

  • 5. 引入枚举类型
    定义支付方式枚举,避免硬编码字符串:
    java
    enum PaymentMethod {
    WECHAT_PAY, ALIPAY;
    }

在Order类中使用枚举类型存储支付方式。
四、总结

  • 优点:
    基本实现了题目要求的功能,类结构清晰,通过组合关系实现了数据关联。
    输入输出逻辑完整,能够正确计算计费重量、运费,并处理超载情况。
  • 不足:
    部分类职责不够单一(如Goods和Order),违背单一职责原则。
    硬编码问题(费率、支付方式)导致扩展性不足,违背开闭原则。
    输入校验和异常处理缺失,程序健壮性较差。
  • 提升方向:
    严格遵循设计原则,通过重构解耦职责,提高代码可维护性。
    合理应用设计模式(策略、工厂、单例等),增强系统扩展性。
    完善输入校验和异常处理,提升程序鲁棒性。
    分离业务逻辑与界面逻辑,便于后续扩展(如添加 GUI 界面)。

通过本次实践,深刻理解了面向对象设计原则的重要性,在后续开发中需提前规划类职责和扩展点,避免过度耦合导致的维护困难

题目集9航空货运管理系统

一、设计与分析

  • 1. 类设计与单一职责原则
    Customer及其子类: 抽象类Customer定义客户基础信息和折扣率接口,IndividualCustomer和CorporateCustomer实现具体折扣逻辑,职责分离清晰,符合单一职责原则。
    Goods及其子类: 抽象类Goods封装货物基础信息和体积重量计算,子类(NormalGoods/ExpediteGoods/DangerousGoods)实现不同类型货物的费率计算。
    潜在问题: 费率计算逻辑仍在子类中硬编码,可进一步拆分为策略类,使Goods仅负责数据存储。
    Order类: 负责订单信息管理、总重量 / 费用计算和输出逻辑。
    问题:输出逻辑(printOrderInfo)与业务逻辑耦合,违背单一职责,可拆分为独立的OrderPrinter类。
    枚举类: CustomerType/GoodsType/PaymentMethod分离类型数据,符合单一职责。
  • 2. 里氏代换原则
    Customer子类(IndividualCustomer/CorporateCustomer)和Goods子类均正确重写父类抽象方法,确保子类可替换父类,符合里氏代换原则。
  • 3. 开闭原则
    新增货物类型: 需新建Goods子类并修改main方法中的switch语句,违背开闭原则。
    改进方向: 使用工厂模式动态创建货物对象,避免修改原有代码。
    新增用户类型: 需新建Customer子类并修改main方法中的条件判断,同样违背开闭原则。
    改进方向: 通过反射或策略模式解耦类型创建逻辑。
  • 4. 合成复用原则
    Order类通过组合Customer/Flight/Goods对象实现功能,而非继承,符合合成复用原则。
  • 5. 依赖倒转原则
    Order依赖抽象类Customer和Goods,而非具体实现类(如IndividualCustomer/NormalGoods),符合依赖倒转原则。
  • 6.深入分析



    方法情况
    方法调用语句: 有 39 条方法调用语句,表明代码中方法之间的交互较为频繁 。
    最复杂方法: 是 Main.main() 方法,位于 252 行。其复杂度为 8 ,包含 53 条语句,最大块深度为 5,调用次数为 31 。说明该方法逻辑相对复杂,可能承担了较多功能,代码可读性和维护性可能面临挑战,可考虑进行功能拆分。
    代码注释
    带注释的行百分比: 仅 0.3% ,说明代码中注释严重不足,不利于他人(甚至自己后续)理解代码逻辑,在团队协作或长期维护时会造成困难。
    分支情况
    分支语句百分比 :19.7%,表示代码中存在一定量的条件判断逻辑,可能使代码逻辑流向复杂。
    代码复杂度
    平均复杂度: 为 8.00 ,整体代码复杂度较高,可能存在逻辑嵌套、条件判断复杂等问题,增加了代码理解和调试难度。
    最大复杂度: 为 8 ,且最大块深度为 5 ,说明代码中存在深层嵌套结构,如多层 if - else、循环嵌套等,可通过重构优化

二、踩坑心得

  • 枚举类型处理:
    输入字符串需严格匹配枚举常量(如Individual/Normal),未处理大小写或空格可能导致IllegalArgumentException。
  • 解决方案: 使用trim()处理输入,并增加容错提示(如提示用户输入有效值)。
  • 输入顺序与换行符
    在nextInt()后读取字符串时,需用scanner.nextLine()消耗剩余换行符,否则会读取空字符串。
    示例:输入goodsName前,确保已处理完前面的nextInt()产生的换行符。
  • 抽象类与接口的选择:
    Customer和Goods使用抽象类而非接口,限制了多继承能力(如货物同时属于 “加急” 和 “危险” 类型)。
  • 教训: 若类型需支持多维度扩展,优先使用接口组合(如Discountable/RateCalculable接口)。
  • 折扣率与费率计算顺序:
    题目要求 “运费 = 计费重量 × 费率 × 折扣率”,需确保计算顺序正确。代码中Goods.calculateFreight已正确传入客户折扣率,未出现逻辑错误。

三、改进建议

  • 1. 重构费率计算(策略模式)
    定义RateStrategy接口,各货物类型实现具体费率计算:
    interface RateStrategy {
    double getRate(double chargeableWeight);
    }
    class NormalRateStrategy implements RateStrategy {
    public double getRate(double cw) { /* 实现普通货物费率逻辑 */ }
    }
    // 加急、危险货物策略类同理

在Goods中注入策略对象,消除子类中的条件判断:
abstract class Goods {
private RateStrategy rateStrategy;
public Goods(RateStrategy rateStrategy) {
this.rateStrategy = rateStrategy;
}
public double getRate() {
return rateStrategy.getRate(getChargeableWeight());
}
}

  • 2. 工厂模式创建货物对象
    新增GoodsFactory消除main方法中的switch语句:
    class GoodsFactory {
    public static Goods createGoods(GoodsType type, int id, String name, double... params) {
    RateStrategy strategy = switch (type) {
    case Normal -> new NormalRateStrategy();
    case Expedite -> new ExpediteRateStrategy();
    case Dangerous -> new DangerousRateStrategy();
    };
    return new Goods(id, name, params[0], params[1], params[2], params[3], type, strategy);
    }
    }

  • 3. 分离输出逻辑(单一职责)
    将Order.printOrderInfo中的输出代码移至独立的OrderPrinter类:
    class OrderPrinter {
    public static void printOrder(Order order) {
    // 统一处理格式,使用常量定义输出模板
    final String TEMPLATE = "客户:%s(%s)订单信息如下:\n%s";
    System.out.printf(TEMPLATE, order.getCustomer().getName(), ...);
    }
    }

  • 4. 增强输入校验
    在枚举类型转换前增加合法性校验,避免程序崩溃:
    public static GoodsType safeParseGoodsType(String input) {
    try {
    return GoodsType.valueOf(input.trim());
    } catch (IllegalArgumentException e) {
    throw new IllegalArgumentException("无效的货物类型,请输入Normal/Expedite/Dangerous");
    }
    }

  • 5. 依赖倒转原则深化
    将PaymentMethod也抽象为接口(如Payment),支持新增支付方式(如银行转账)而不修改Order类:
    interface Payment {
    String getDisplayName();
    double processPayment(double amount);
    }
    class WechatPayment implements Payment

四、总结

  • 优点:
    引入抽象类和枚举,清晰分离客户类型、货物类型和支付方式,符合依赖倒转原则。
    通过子类实现不同客户折扣和货物费率,基本满足里氏代换原则。
    组合关系的使用(如Order包含Goods数组)符合合成复用原则。
  • 不足:
    货物类型和客户类型的创建逻辑仍依赖具体类(switch/if-else),违背开闭原则。
    输出逻辑与业务逻辑耦合在Order类中,违背单一职责原则。
    枚举类型输入缺乏容错处理,程序健壮性不足。
  • 提升方向:
    全面应用策略模式和工厂模式,将可变部分(费率计算、对象创建)封装为独立模块。
    分离 “数据处理” 与 “界面展示”,提升代码可维护性。
    增加输入校验和异常处理,提供友好的错误提示。
    使用接口替代部分抽象类,支持更灵活的多态扩展(如混合货物类型)。
    通过本次改进,代码结构将更符合面向对象设计原则,后续新增需求(如其他货物类型、折扣策略)时,只需扩展新类而不修改原有逻辑,真正实现 “对扩展开放,对修改关闭”。

整体总结
这两段 Java 代码围绕航班货物运输订单管理系统展开,在设计与实现上各有特点,也存在一定的改进空间。

在设计方面,代码通过定义多个类(如Customer、Goods、Flight、Order等)以及使用枚举类型,对系统中的不同实体进行了合理抽象和封装,初步构建了较为清晰的结构。同时,运用继承和多态(如Customer和Goods的子类)实现了不同类型客户和货物的差异化处理,体现了面向对象编程的思想。然而,在一些关键设计原则的遵循上仍有欠缺。例如,部分类的职责不够单一,像Order类同时承担业务逻辑计算和输出展示功能;在开闭原则上,新增客户类型或货物类型时需要修改较多现有代码,可扩展性受限。

从代码复杂度来看,通过分析代码度量指标可知,第一段代码整体复杂度相对尚可,但部分方法(如Goods.getRate())复杂度较高;第二段代码中Main.main()方法复杂度达到 8,存在大量语句和方法调用,代码的深度和复杂度有所增加。这反映出在方法设计和逻辑组织上需要进一步优化,以降低代码理解和维护的难度。

在代码质量方面,两段代码均存在输入校验不完善的问题,可能导致非法数据进入系统,影响程序的稳定性。异常处理也不够全面,未能充分考虑运行过程中可能出现的各种异常情况。此外,代码中存在硬编码字符串,不利于国际化和本地化,且注释占比较低,降低了代码的可读性。

针对这些问题,可采取一系列改进措施。在设计模式上,进一步应用策略模式、工厂模式等,将变化部分(如费率计算、对象创建)进行封装,提高代码的可扩展性和可维护性;在代码结构上,分离业务逻辑和输出逻辑,使各个类职责更加单一;同时,加强输入校验和异常处理,完善代码注释,提升代码的健壮性和可读性。

总体而言,这两段代码为航班货物运输订单管理系统的开发奠定了基础,通过持续优化和改进,有望成为更加高效、稳定且易于维护的系统。

posted @ 2025-05-20 23:49  24201725-孙思成  阅读(33)  评论(0)    收藏  举报