第二次Blog的总结-航空货运管理系统

一:前言

题目集8~9 以航空货运管理系统为背景,聚焦航空运费计算与订单处理业务,核心考察面向对象编程中的类设计,以及单一职责、里氏代换、开闭、合成复用和依赖倒转五大原则的实际运用,旨在检验开发者构建高内聚、低耦合软件系统的能力。以及两次题目集的迭代,引领我们关注类职责的细化拆分、系统扩展性设计、继承体系的合理性,让我们对类与类之间的关系有了更深入的了解,引导我们从 “功能实现” 向 “可维护、可扩展的系统架构设计” 进阶。

1. 知识点

以航空货运管理系统为载体,核心考察面向对象编程的类设计与五大设计原则的运用。其中包括根据货物重量与体积计算计费重量、结合货物类型费率和用户折扣率算出基础运费等业务逻辑的类实现;同时要求严格遵循单一职责原则拆分类功能,运用里氏代换原则确保继承体系的可靠性,通过开闭原则实现系统的可扩展性,借助合成复用原则提升代码复用性,利用依赖倒转原则降低类间耦合度,全面检验设计者的架构设计与编程实践能力。

2. 题量

两次题目集分别都只有3道题,点线面问题重构,雨刷程序功能扩展设计,魔方,点线面的再重构问题,以及两次航空货运管理系统的类设计,题量不太大。

3. 难度

相较于5~7题目集考察思维逻辑来说,这次题目集对类设计的考察,难度下降了很多,但是对于如何设计才能使代码运行更高效,又对我们提出来新的挑战。

 

二:设计与分析

1. 第一次作业

题目要求:

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

输入格式:
按如下顺序分别输入客户信息、货物信息、航班信息以及订单信息。

输出格式:
• 如果订单中货物重量超过航班剩余载重量,程序输出The flight with flight number:航班号 has exceeded its load capacity and cannot carry the order. ,程序终止运行。
• 如果航班载重量可以承接该订单,输出如下:

 类图:

 

类设计思路:

Person类:一开始设计的时候还没有想到单独抽出来一个人物类出来,但是在我读题目时发现客户有姓名,电话,地址,发件人,收件人也有姓名,电话,地址,因此我打算抽出一个人物类,用来封装姓名,电话,地址这些个人通用信息,作为Customer、Sender、Receiver类的父类。

Customer类:分析输入输出,客户要提供姓名电话等个人信息,但是不需要参与额外的计算,于是我把客户分为一个单独的类,继承Person类,存放客户的个人信息。

Sender类和Receiver类:跟Customer类一样,仅需提供个人信息,所以把它们也单独作为一个类。

Goods类:因为要输出货物明细报表,因此我设计了货物类,用于描述货物信息,包含goodsid、goodsName、width、length、height、weight等属性及操作方法,对货物信息的抽象封装,便于管理和操作货物相关数据。

Order类:根据题目要求的货物明细报表,不止一个货物,于是我设计了订单类,通过LinkedList<Goods>属性管理多个货物,提供添加货物和计算总重量等方法,实现了对订单中货物集合的管理。

ClientDetails类:题目还要求输出订单信息报表,需要航班号,订单号,发件人,收件人的信息等,于是我又设计了一个客户明细类,整合了orderId、orderDate等订单信息,以及Sender、Receiver、Payment等相关对象,将订单涉及的发货人、收货人、支付方式等信息聚合,形成完整订单详情类。

Payment类:题目需要选择微信支付还是支付宝支付,于是我设计了一个抽象类,定义支付相关的抽象方法pay()。

WeChat类和Alipay类:继承自Payment类,分别实现微信和支付宝的具体支付逻辑。

Flight类:将提供的航班的flightId、originAirport、arrivalAirport、flightDate、maxWeight等信息及操作方法封装起来,用于管理航班信息。

Controller类:空运费计算需综合考虑货物重量 / 体积、结合不同货物类型费率 。因此我设计了控制类,持有Order对象,提供获取订单、计算(可能涉及运费等计算逻辑)、获取日期等方法,能够协调和控制订单相关业务逻辑。

设计原则的体现:

 单一职责原则

 类功能明确:每个类专注于特定功能。如Goods类仅负责描述货物信息,不涉及订单管理或支付等其他功能;Flight类只管理航班的flightId、originAirport等航班相关信息,做到一个类一个主要职责,降低类的复杂度,提高代码的可维护性。
 方法职责单一:以Order类为例,addGoods(Goods goods)方法专门用于向订单中添加货物,getTotalWeight()专注计算订单货物总重量,方法功能单一,避免方法内逻辑混杂。

• 里氏代换原则:Customer、Sender、Receiver类继承自Person类,它们可以在程序中替代Person类使用。

• 开闭原则:Payment为抽象类,定义了pay()抽象方法。WeChat和Alipay类继承自Payment并实现pay()方法。

• 合成复用原则:ClientDetails类中,通过组合Sender、Receiver、Payment等对象来构建完整订单详情,而非继承这些类。

• 依赖倒转原则:Payment作为抽象类,WeChat和Alipay子类依赖于抽象的Payment。

复杂度分析:

 

Metrics Details For File '航班管理1.java'
--------------------------------------------------------------------------------------------

Parameter                Value
=========                =====
Project Directory            D:\电梯代码\航务管理系统1\
Project Name                8
Checkpoint Name                8
File Name                航班管理1.java
Lines                    525
Statements                166
Percent Branch Statements        0.6
Method Call Statements            18
Percent Lines with Comments        1.7
Classes and Interfaces            4
Methods per Class            14.50
Average Statements per Method        2.55
Line Number of Most Complex Method    11
Name of Most Complex Method        Sender.Sender()
Maximum Complexity            1
Line Number of Deepest Block        16
Maximum Block Depth            2
Average Block Depth            0.66
Average Complexity            1.00

--------------------------------------------------------------------------------------------
Most Complex Methods in 3 Class(es):    Complexity, Statements, Max Depth, Calls

Person.getAddress()            1, 1, 2, 0
Person.getName()            1, 1, 2, 0
Person.getPhone()            1, 1, 2, 0
Person.Person()                1, 3, 2, 0
Person.Person()                1, 0, 0, 0
Person.setAddress()            1, 1, 2, 0
Person.setName()            1, 1, 2, 0
Person.setPhone()            1, 1, 2, 0
Receiver.Receiver()            1, 1, 2, 1
Receiver.Receiver()            1, 0, 0, 0
Sender.Sender()                1, 1, 2, 1
Sender.Sender()                1, 0, 0, 0

--------------------------------------------------------------------------------------------

主要问题:

1. 方法复杂度不均衡:从 “Most Complex Methods” 列表看,部分方法复杂度虽为 1 看似简单,但像Person.Person() 、Sender.Sender() 、Receiver.Receiver() 等方法存在两种不同复杂度表现(有语句数为 0 和非 0 情况 ),意味着代码逻辑不统一或存在冗余定义,后期维护易混乱。

2. 最大块深度和平均块深度:最大块深度为 2 ,平均块深度 0.66,虽不算高,但结合方法复杂度和语句分布,在嵌套逻辑处理上不够简洁。

3. 平均语句数较低但分布不均:平均每个方法语句数为 2.55,整体较低。不过在不同方法中语句数差异大,如Person.Person() 有 3 条语句,部分方法有 1 条,还有为 0 的,可能导致方法功能划分过细或过粗,不利于代码理解与复用。

4. 注释比例低:带注释的行数百分比仅 1.7 ,代码缺乏注释会使代码逻辑难理解,尤其是团队协作或后期自己维护时,增加解读成本,不利于代码长期维护和迭代。

改进心得:

第一次做设计的时候没有仔细观察代码,没能发现有些地方可以进行再拆分,导致方法冗余,结构变得复杂。因此我期望在后续的代码里可以改进以下方面。

1. 消除代码冗余:提取重复代码为公共方法,比如将计算体积重量和计费重量的逻辑提取到一个独立方法中,在Order 类和Controller 类中调用,降低代码复杂度,优化雷达图中的复杂度指标。

2. 添加注释:在关键代码段、复杂业务逻辑处添加注释,说明代码功能和设计意图,提高代码可读性,增加注释比例,优化 “% Comments” 指标。

 

2. 第二次作业

题目要求:

航空快递以速度快、安全性高成为急件或贵重物品的首选。本题目要求对航空货运管理系统进行类设计,要求满足面向对象设计原则中的单一职责原则、里氏代换原则、开 闭原则以及合成复用原则、依赖倒转原则。

 

输入格式:
按如下顺序分别输入客户信息、货物信息、航班信息以及订单信息。

输出格式:
• 如果订单中货物重量超过航班剩余载重量,程序输出The flight with flight number:航班号 has exceeded its load capacity and cannot carry the order. ,程序终止运行。
• 如果航班载重量可以承接该订单,输出如下:

类图:

 

 

 类设计思路:

 

 

 

1. Good类新增:根据题目计算运费需要根据不同的货物类型来计算。所以我在Good类的构造方法参数新增了GoodsStyle(货物类型),并增加了对应的GoodsStyle()和setGoodsStyle()方法。补充了货物类型的分类信息,使商品实体的属性更完整,用于区分不同运输规则(如易碎品、普通货物等)。

2. Order类新增:题目又新加了根据不同的客户类型计算折扣,所以我在Order类中新增了setCustomStyle()方法。用于设置订单的客户类型(如个人用户和集团用户),结合运费计算逻辑,可支持不同客户等级的费率优惠,增强了订单业务的灵活性。

3. 新增Cash类:基于第一次作业支付方式已经做成了抽象类,第二次作业要求增加了现金支付选项,我就直接新增了一个Cash类继承抽象类支付方式,不需要额外改动代码。

设计原则的体现:

• 单一职责原则:Goods类仅负责商品属性(类型、尺寸、重量等)的封装,职责单一。Payment抽象类及其子类(WeChat、Alipay、Cash)仅处理支付逻辑,符合单一职责。

• 里氏代换原则:Customer、Sender、Receiver均继承自Person,并复用其父类的属性(姓名、电话、地址)和方法(getName、setPhone等),子类对象可替代父类使用。WeChat、Alipay等子类实现Payment抽象类的pay方法,可在需要Payment的场景中无缝替换(如ClientDetails类中使用Payment类型成员)。

• 开闭原则:新增支付方式(如Cash类)时,只需继承Payment抽象类并实现pay方法,无需修改原有支付逻辑。Goods类通过goodsStyle属性支持不同货物类型(如Normal、Dangerous)的运费规则扩展,Controller的getRate方法通过条件判断处理新增类型,虽未完全隔离变化,但避免了对核心计算逻辑的直接修改。

• 合成复用原则:Order类包含LinkedList<Goods>类型的list成员,通过组合管理商品列表,而非继承Goods类。ClientDetails类组合了Sender、Receiver、Payment等对象,体现 “Has-A” 关系,符合合成复用原则。

• 依赖倒转原则:Payment抽象类定义支付接口,WeChat、Alipay等细节类依赖该抽象,高层模块(如ClientDetails、Main)通过Payment接口引用具体支付实现,符合依赖倒转原则

复杂度分析:

Metrics Details For File 'plane2.java'
--------------------------------------------------------------------------------------------

Parameter                Value
=========                =====
Project Directory            D:\电梯代码\航空管理系统2\
Project Name                9
Checkpoint Name                9
File Name                plane2.java
Lines                    592
Statements                182
Percent Branch Statements        0.5
Method Call Statements            18
Percent Lines with Comments        1.5
Classes and Interfaces            5
Methods per Class            12.60
Average Statements per Method        2.59
Line Number of Most Complex Method    11
Name of Most Complex Method        Sender.Sender()
Maximum Complexity            1
Line Number of Deepest Block        16
Maximum Block Depth            2
Average Block Depth            0.65
Average Complexity            1.00

--------------------------------------------------------------------------------------------
Most Complex Methods in 4 Class(es):    Complexity, Statements, Max Depth, Calls

Person.getAddress()            1, 1, 2, 0
Person.getName()            1, 1, 2, 0
Person.getPhone()            1, 1, 2, 0
Person.Person()                1, 3, 2, 0
Person.Person()                1, 0, 0, 0
Person.setAddress()            1, 1, 2, 0
Person.setName()            1, 1, 2, 0
Person.setPhone()            1, 1, 2, 0
Receiver.Receiver()            1, 1, 2, 1
Receiver.Receiver()            1, 0, 0, 0
Sender.Sender()                1, 1, 2, 1
Sender.Sender()                1, 0, 0, 0
WeChat.pay()                1, 1, 2, 0

--------------------------------------------------------------------------------------------

 主要问题:

1. 方法复杂度不均衡:部分方法(如Person.Person() 、Sender.Sender() 、Receiver.Receiver() )存在两种不同复杂度表现(语句数有 0 和非 0 情况 ),意味着代码逻辑不统一,存在冗余定义或功能划分混乱的问题,不利于后期维护和代码的稳定性。

2. 块深度相关问题:虽然最大块深度为 2 ,平均块深度 0.65 不算高,结合方法复杂度和语句分布情况来看,嵌套逻辑处理不够简洁高效,可能会影响代码的执行效率和可维护性。

3. 代码注释匮乏:带注释的行数百分比仅 1.5% ,注释严重不足。这会导致代码可读性差,尤其是在复杂业务逻辑或关键代码段处,难以快速理解代码意图和功能逻辑,增加解读成本和维护难度。

相较于第一次改进的地方:

平均块深度:提取重复代码为公共方法,将计算体积重量和计费重量的逻辑提取到一个独立方法中。使得第一次分析中平均块深度为 0.66,此次为 0.65 ,嵌套逻辑处理上有优化,使整体平均块深度稍有降低。

改进心得:

1. 重构复杂方法:对Person.Person() 、Sender.Sender() 、Receiver.Receiver() 等存在复杂度差异的方法进行审查和重构。明确方法功能,删除冗余方法;若方法功能有遗漏,补充完整逻辑;对于功能相近的方法,尝试合并或统一逻辑,使代码逻辑更清晰、简洁。
2. 优化方法功能划分:对语句数差异较大的方法,重新审视其功能。将功能过于复杂的方法拆分成多个功能单一的方法;对于功能简单且相近的方法,考虑是否可以合并,使方法功能划分更合理,提高代码的可理解性和复用性。

三:踩坑心得

1. 在一开始设计的时候,除了基础的大纲时,在编写代码时对于输出格式一筹莫展,因为货物明显报表需要每个货物的计费重量,以及要针对每个货物的计费重量进行匹配运率,一直不知道怎么设计,浪费了很多时间,后来才知道可以将方法做成集合的形式,将要算的存入集合就很好的解决了问题,对于方法的类型我要多多思考可能性,不要只局限于double 和void 类型。

2. 在第二次作业中,对一些细节的把控也没做好。

 

有一次提交时,有非零返回,于是我针对测试点进行排查,一开始以为是我关于计费重量或者计算运费算错误了,又输入不同的测试用例发现,在输入现金支付时,异常情况处理不对,我才发现是判断支付方式的switch 语句缺陷。在现金支付那里少了一个break语句。以后对于细节的把控要注意反思。

错误的代码:

 改正的代码:

 

3. 在设计时,也要注意代码的冗余。可以多观察下有没有共同的代码,整合在一起,能大大降低代码的复杂度。

第一次关于计算运费和计算运率的代码:

 第二次作业:

 将获取lastWeight提取出一个方法,以后计算运费和计算运率只需调用这个方法,减少了很多代码量。

 

四:改进建议

1. 降低整体复杂度:提取重复代码。如Controller类中计算运费、运率等方法存在重复计算体积重量等逻辑,将这些公共逻辑提取成独立方法,降低方法复杂度,进而优化平均复杂度指标。

2. 优化块深度:简化嵌套逻辑。在计算运费和计算运率时,大幅运用了if - else 等嵌套结构,可以通过合并条件、提前返回等方式减少嵌套层数,降低最大块深度和平均块深度。

3. 大幅增加注释:当前注释比例极低,在类、方法以及复杂逻辑代码段添加注释。类注释说明功能和职责;方法注释写明输入、输出、功能;复杂逻辑注释解释实现思路。如在计算运费的方法中,注释每段代码的计算目的。

 

五:总结

1. 学习收获

面向对象设计原则的实践

 

  • 通过航空货运管理系统的类设计,深入理解并应用了单一职责、里氏代换、开闭、合成复用和依赖倒转五大原则。例如,将Payment设计为抽象类,通过子类实现具体支付方式,体现了开闭原则和依赖倒转原则;ClientDetails类组合SenderReceiver等对象,而非继承,符合合成复用原则。
  • 学会通过继承1(如CustomerSender继承Person)实现代码复用,通过多态(如支付方式的扩展)提升系统可扩展性。

 

类设计与业务逻辑的解耦

      •  明确类的职责划分,如Goods类仅封装货物属性,Order类管理货物集合,Controller类专注运费计算,避免类职责混杂,符合单一职责原则。
      •  掌握通过提取1公共方法(如将计算计费重量的逻辑提取为caculateLastWeights)减少代码冗余,降低方法复杂度。

代码复杂度与可读性的优化
      • 意识到方法复杂度不均衡(如构造方法存在语句数为 0 的冗余定义)和注释匮乏(注释比例仅 1.5%-1.7%)的问题,需通过重构和添加注释提升代码可维护性。

2. 需进一步学习及研究的方向

设计模式的深入应用
      • 目前对策略模式(如不同货物类型的费率计算)和工厂模式(如对象创建逻辑)的应用仍停留在基础层面,需进一步学习如何通过设计模式完全隔离变化,避免Controller             中大量条件判断,更彻底地遵循开闭原则。
异常处理与代码健壮性
      • 输入验证逻辑薄弱(如未处理非法输入类型),导致程序易崩溃(如非零返回错误)。需系统学习 Java 异常处理机制,在关键业务节点添加输入校验和异常捕获。
性能优化与代码规范
      • 对集合类(如LinkedList与ArrayList)的性能差异理解不足,需深入研究数据结构特性,选择更优容器提升代码效率。

 

posted @ 2025-05-24 19:57  种自己的太阳  阅读(34)  评论(0)    收藏  举报