JAVA_OOP_航空货运管理系统

目录

    1. 前言
    1. 航空货运管理系统(类设计)
    • 题目分析
    • 2.1 我的设计
    • 2.2 我的分析
    • 2.3 踩坑心得
    1. NCHU_航空货运管理系统(继承与多态)
    • 题目分析
    • 3.1 我的设计
    • 3.2 我的分析
    • 3.3 踩坑心得
    1. 改进建议
    1. 总结


前言

在上一次电梯调度系统对我们在算法和类设计的磨练后,进度加快后,对知识的掌握更加牢固和熟练,这次的PTA对大家的压力都少了很多,做的快的同学基本上都在一天之内就提交正确。对于我来说虽然痛苦感比之前少了些,但是也在提高代码的可扩展性及可复用性,学习使用其他知识点如枚举类方面挣扎了良久。

  • 所涉及知识点
  1. 运用七大设计原则设计出合理的类,准确把握类与类之间的关系。
  2. 继承与多态的灵活使用。
  3. 抽象类与抽象方法。
  • 题量

与之前相当,每次作业为3题,前两小题基本上均为后两题打基础。

  • 难度

难度较上次的算法低很多,重点还是在类设计上,很考验设计思维,要考虑类的扩展性,类与类间的关系,在提高代码复用性的同时要降低耦合性,以及是否符合七大设计原则。虽没有那么烧脑,但是需要细细品。

航空货运管理系统(类设计)

题目分析

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

航空公司提供如下信息:

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

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

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

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

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

4.支付方式(支付宝支付、微信支付)

如果订单中货物重量超过航班剩余载重量,程序输出The flight with flight number:航班号 has exceeded its load capacity and cannot carry the order.程序终止运行。
注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独计费。

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

输出格式:

2.1我的设计

2.2我的分析

  1. 首要分析的是要分几个类:
    从输入的对象就可以看出,要分为客户、货物、航班、订单、发件人和收件人这几类,当然这还是最浅显的。

    其次,这是个很经典的订单问题,MVC设计模式用上,所以应当还有控制器Controller类,处理客户与订单之间的业务逻辑,视图View类,减小耦合,遵循单一职责原则。

    然后写完后发现不符合题目中要求的接口隔离原则,故将原本放在货物类中的计算费率提出来,写一个Rate Sttrategy接口,用Rate类继承接口返回费率。

    最后,优化的时候为了符合单一职责原则,将订单的有效性单独提出来作为一个类OrderValidator类,用来判断订单是否有效。


  1. 其次分析的是数据的存储方式:
    每次输入中,客户,航班,订单,发件人和收件人只有一个,而货物可以有多个,每个货物的属性都可以不一致,故用ArrayList来储存货物Good.

  1. 最后分析七大设计原则在代码中的体现:
    这段代码体现了多个面向对象设计原则,下面我将分析七大设计原则在这段代码中的体现情况:
    1. 单一职责原则 (Single Responsibility Principle)
    • 良好体现:各个类的职责划分清晰
    • CustomerFlightGoodOrderPostman只负责封装自身数据
    • Rate只负责计算费率
    • OrderValidator只负责验证订单
    • Controller负责协调各个组件
    • View只负责显示信息
    1. 开闭原则 (Open/Closed Principle)
    • 部分体现:通过策略模式实现费率计算
    • RateStrategy接口和Rate实现使得费率计算可以扩展而不修改现有代码
    • 但其他部分如验证逻辑没有类似的扩展机制
    1. 里氏替换原则 (Liskov Substitution Principle)
    • 未明显体现:代码中没有明显的继承层次结构
    1. 接口隔离原则 (Interface Segregation Principle)
    • 良好体现:RateStrategy接口非常精简
    • 只包含一个必要的方法rate()
    1. 依赖倒置原则 (Dependency Inversion Principle)
    • 部分体现:Controller依赖抽象(RateStrategy)
    • 但其他类之间多是具体依赖
    • View直接依赖具体Controller而非抽象
    1. 迪米特法则/最少知识原则 (Law of Demeter)
    • 部分违反:View类通过controller获取多层对象
    • controller.getCustomer().getName()直接访问了多层
    • 更好的做法是controller提供getCustomerName()方法
    1. 合成复用原则 (Composite Reuse Principle)
    • 良好体现:大量使用对象组合而非继承
    • Controller组合了多个其他类的实例
    • 整个系统几乎没有使用继承
  • 其他值得注意的设计:

    1. 策略模式:RateStrategyRate实现了策略模式,使费率计算算法可以独立变化

    2. MVC模式:整体结构类似MVC

    • Model: Customer, Flight, Good等实体类

    • View: View

    • Controller: Controller

      1. 封装性:所有类字段都是private,通过getter/setter访问

  1. 代码分析:

    • 注释写得少,可读性和维护性不高。
    • 每个类方法数较多,类的职责划分不够单一,违背单一职责原则。
    • 平均复杂度偏低:某些方法可能过于简单,但是一个方法不能承担过多职责,增强复用,所以问题不大。
    • 其他如平均深度、最大复杂度等在合理范围。

2.3踩坑心得:

自己在计算总重量时用了实际重量,正确的应该是用计费重量导致一开始只能通过输入错误的测试点。



航空货运管理系统(继承与多态)

题目分析

第二次为第一次的迭代,较上一次增加如下需求:

  • 货物分为普通货物、危险货物和加急货物三种。
  • 三种货物费率不同。
  • 支付方式增加了现金支付。
  • 客户分为个人用户和集团用户,个人用户9折优惠,集团用户8折优惠。

3.1 我的设计


3.2 我的分析

  1. 对可扩展性的思考

拿到题目,我一开始是直接在客户、货物、View类中用if-else嵌套循环,实现不同对象下不同的输出,虽然测试用例过了(没提交)。但很明显,这样不符合多态的要求,代码的可扩展性以及可复用性都极低。

然后,我联想到了之前,在写单步调度电梯的时候,也是根据direction和state作为属性(?)来判断电梯的运行,那这里的客户、货物、支付类型也同理可以作枚举型!

但是之前在单步调度电梯对enum的使用仅限于声明方向和状态,当我想在这里复现的时候均以失败告终,因为我还对enum的用法不熟悉,只会运用基本用法。后面在网上查找资料的时候才发现,虽然用枚举类型单纯存储类型,可以一定程度上提高程序的扩展性,但是代码还是比较冗余,依然要用多个if-else嵌套循环,主要是在计算费率方面,还是没有解决代码冗余问题。

学习之后,就将货物类的类型及其费率一同放进了一个枚举类做参数,将计算费率的功能也在此实现,原来计算费率的类的内容就变成传type的方法就行。将客户类的类型及其折扣一同放进了一个枚举类做参数,将计算折扣费用的功能也在此实现,支付方式相对来说简单些,只存储了一个变量,无需实现其他功能。

这里除了需要定义枚举类,还需要属性,分别为费率和类型和折扣率,以及构造方法。除此之外,因为输入的类型都是字符型,还需要将字符型转化为枚举型的方法。最后再根据每个枚举类需要实现的功能为其写方法。

下面是顾客类的枚举类型。举例

enum GoodType {
 Normal("Normal", 35, 30, 25, 15),
 Expedite("Expedite", 60, 50, 40, 30),
 Dangerous("Dangerous", 80, 50, 30, 20);

 private final String typeName;
 private final double rate1;
 private final double rate2;
 private final double rate3;
 private final double rate4;

 GoodType(String typeName, double rate1, double rate2, double rate3, double rate4) {
     this.typeName = typeName;
     this.rate1 = rate1;
     this.rate2 = rate2;
     this.rate3 = rate3;
     this.rate4 = rate4;
 }

 public double getRate(double weight) {
     if (0 <= weight && weight < 20) return rate1;
     else if (20 <= weight && weight < 50) return rate2;
     else if (50 <= weight && weight < 100) return rate3;
     else return rate4;
 }

 //// 从字符串转换为枚举实例(忽略大小写)
 public static GoodType fromString(String type) {
     for (GoodType goodType : GoodType.values())
         if (type.equals(goodType.typeName)) return goodType;
     throw new IllegalArgumentException();
 }
}
  1. 七大设计原则在代码中的体现分析
  • 单一职责原则 (Single Responsibility Principle)

    • Good类只负责货物信息的管理
    • Customer类只负责客户信息的管理
    • Flight类只负责航班信息的管理
    • Order类只负责订单基本信息的管理
    • Postman类只负责快递员信息的管理
    • Rate类只负责计算费率
  • 开闭原则 (Open/Closed Principle)

    • 使用枚举类型GoodTypeCusTypePayType来管理不同类型,便于扩展新类型
    • 使用RateStrategy接口和Rate实现类,便于扩展新的费率计算策略
  • 里氏替换原则 (Liskov Substitution Principle)

    • Rate类实现了RateStrategy接口,可以无缝替换
    • 所有枚举类型都提供了fromString方法,行为一致
  • 接口隔离原则 (Interface Segregation Principle)

    • RateStrategy接口非常精简,只包含一个必要方法
    • 没有出现臃肿的接口
  • 依赖倒置原则 (Dependency Inversion Principle)

    • Controller依赖于抽象的RateStrategy接口而非具体实现
    • View依赖于Controller抽象而非具体实现
  • 迪米特法则/最少知识原则 (Law of Demeter)

    • View类通过Controller获取数据,不直接访问其他类的内部细节
    • 大多数类只与直接相关的类交互
  • 合成复用原则 (Composite/Aggregate Reuse Principle)

    • Controller通过组合方式使用RateStrategyOrderValidator
    • Main类通过组合方式构建整个系统

  1. 代码分析

    • 注释写得少,可读性和维护性不高。
    • 平均每个类5.78个方法数较合理,平均每个方法0.90个语句(异常低,可能统计方式特殊或存在大量简单方法)。
    • GoodType.getRate():复杂度8(非常高),包含多层条件判断(weight范围判断)。
    • 其他如平均深度、最大复杂度等在合理范围。

3.3踩坑心得

  1. 主要还是在怎么用枚举类表示不同的类型上面进行了很多无用的尝试,一直在用旧知识尝试新功能,后面才知道需要用构造方法和属性。
  2. 其次就是创建完枚举类后,想直接用equals()函数确定类型对象,结果都报错说传参类型不一致,后面才知道可以有字符型转枚举型


改进建议

  1. 好吧这次还是注释量很低,但比上次要稍好一些,在重难点处会稍做注释。注释量要争取节节高,慢慢来。
  2. GoodType.getRate()的复杂度太高,需要方法重构,查找资料后指出可以用查表法,但这个还没有学习到,后续需针对性学习。
  3. 支付方式的枚举类不需要构造方法和属性,同时为了代码的可拓展性,这个类可以进一步抽象为策略模式。
  4. 需求需求还是需求,要看清需求再下笔,要不然一直在原地兜圈子。
  5. 因为收件人和发件人属性和方法极为类似,提交完之后觉得可以将收件人和发件人作为一个“人”抽象下的两个类,减少代码冗余。


总结

  1. 虽然这次需求理解难度上没有之前大,但还是要注意需求的理解。
  2. 写完了代码不能拘泥于过测试点,还要进一步重构使其符合七大设计原则,思考如何提高代码的可扩展性和可复用性。
  3. 减少分支语句占比和方法调用。我的GoodType.getRate()代码中充斥着较多if-else语句,使运行效率低,后面要学习怎么把这一大串的暴力改好。
  4. 还有很多知识点不会但很好用,比如这次题目中的DecimalFormat类,以及枚举类的高级用法,字符型转各种类型,查表法,策略模式等等,还是要多学多用。
posted @ 2025-05-25 09:13  YXYA  阅读(20)  评论(0)    收藏  举报