第二次Blog作业——航空管理系统迭代

一、前言
经过第一次Blog作业,我对迭代作业有了初步的了解和认识,由于上次并未做出来电梯系统,此次我在航空管理系统中,一方面此次题目相较于上次比较简单,另一方面我也花费了更多的精力在上面,此次作业我也是成功的做了出来。这次作业的难度不大,主要考查的是继承与多态,这次作业与上次迭代相比,最主要的区别是没有给出类图,而是让我们自己设计类图,由于此前并未接触过自己设计,画类图对我来说也是个不小的困难,但是在软件开发过程中,这些都是我们必须学会的东西,因此我也是顺利的学会了此次迭代作业的相关内容。在软件开发的世界里,很少有系统能在第一次设计时就达到完美的状态。更多时候,我们需要通过不断的迭代和优化,让代码变得更加健壮、灵活和可维护。从第一次作业到第二次作业,虽然只是一个课程项目的迭代,但其中蕴含的设计思想和改进思路,却给我带来了很多启发。
二、设计与分析
1.第一次航空管理系统设计:
类图:

由此图片可以看出,在第一次的作业中,由于是第一次画类图,我对类图的绘画不是很了解,画的比较简单,并且在各个类的之间的联系不是很明确应该用什么线,导致类图画的不是很符合实际。
第一次读完题目的时候,我发现其实并不难,主要是进行一些简单的计算和输出,主要的难度集中在计算和输出上上面(如下图)

然后根据输入的内容给出一个如下图中格式的输出:

物流系统的雏形设计
由类图可知,第一次我设计了一个基本的物流系统(共有七个类),实现以下核心功能:
客户信息管理
货物信息处理(包括体积重量计算)
配送订单管理
支付处理
航班载重检查
订单信息输出

核心类设计
1.第一次作业的核心类设计主要包括以下几个部分:
2.抽象支付类(Pay):作为支付方式的抽象基类,定义了计算费用、设置和获取账单金额的基本方法。
3.具体支付类(AliPay、WePay):继承自抽象支付类,实现了具体的支付功能。
4.客户信息类(CustomerInformation):存储客户的基本信息,包括编号、姓名、电话和地址。
5.配送类(Delivery):管理配送订单,包括货物列表、发件人和收件人信息、订单日期等。
6.货物类(Goods):存储货物的基本信息,包括编号、名称、尺寸和重量,并实现了体积重量的计算。
7.航班类(Flight):存储航班信息,包括航班号、起降地点、日期和最大载重。
8.主类(Main):程序的入口,处理用户输入和业务逻辑。

核心业务流程
第一次作业的核心业务流程如下:
1.输入客户信息,创建客户对象。
2.输入货物数量和每个货物的信息,创建货物对象,并计算体积重量。
3.输入航班信息,创建航班对象。
4.输入配送信息,创建配送对象,并将货物添加到配送列表。
5.检查航班载重是否超过限制。
6.计算总费用,使用微信支付方式。
7.输出订单信息和货物明细。

设计分析
第一次作业的设计实现了基本的功能需求,但也存在一些明显的不足:
1.支付方式与配送类的耦合度较高:支付类中的计算费用方法直接使用了配送类中的费率计算逻辑,违反了封装原则。
2.缺乏扩展性:如果需要添加新的支付方式或货物类型,需要修改多个类的代码,不符合开闭原则。
3.代码重复:支付类和配送类中都实现了类似的费率计算逻辑,存在代码重复。
4.缺少枚举类型:对于一些固定类型的信息(如客户类型、支付方式),没有使用枚举类型,而是直接使用字符串或条件判断,增加了出错的可能性。
5.主类职责过重:主类不仅处理用户输入,还承担了大量的业务逻辑处理,违反了单一职责原则。

2.第二次航空管理系统设计:
在第二次作业中,我对第一次作业的设计进行了全面的优化和扩展,主要包括以下几个方面:

引入枚举类型
首先,我引入了三个枚举类型,用于表示客户类型、货物类型和支付方式:

使用枚举类型的好处是:
提高了代码的可读性和可维护性,避免了魔法字符串的使用。
限制了合法的取值范围,减少了运行时错误的可能性。
方便后续的扩展,如果需要添加新的类型,只需修改枚举定义即可。

在第一次作业中,支付类与配送类的耦合度较高,支付类的计算费用方法直接使用了配送类的费率计算逻辑。在第二次作业中,我对支付模块进行了重新设计:
1.在抽象支付类中添加了对配送类的引用,通过构造函数传入配送对象。
2.修改了计算费用的方法,调用配送类的费率计算方法。
3.添加了支付类型的属性和 getter/setter 方法。
4.为每种支付方式创建了具体的支付类,继承自抽象支付类。


这样的设计使得支付模块与配送模块之间的耦合度降低,支付类只需要知道配送类的接口,而不需要了解其具体实现。同时,添加新的支付方式变得更加容易,只需要创建一个新的具体支付类,继承抽象支付类即可,符合开闭原则。

增强配送类功能
在第二次作业中,配送类增加了货物类型的属性,并根据货物类型调整了费率计算逻辑:

这样的设计使得系统能够处理不同类型的货物,每种货物类型有不同的费率计算规则。如果需要添加新的货物类型,只需要修改枚举定义和费率计算逻辑即可,提高了系统的扩展性。

优化客户信息类
客户信息类增加了客户类型的属性,用于后续的折扣计算:

根据客户类型的不同,系统可以提供不同的折扣:个人客户享受 9 折优惠,企业客户享受 8 折优惠。这一功能通过在主类中添加一个折扣计算方法实现:

改进主类设计
在第一次作业中,主类承担了过多的职责,包括用户输入处理、业务逻辑处理和结果输出。在第二次作业中,我对主类进行了改进:
1.分离了支付方式的创建逻辑,根据用户输入的支付类型创建相应的支付对象。
2.添加了辅助方法,用于计算折扣金额和获取支付方式的中文名称,使主方法更加简洁。
3.优化了输出格式,使订单信息更加清晰易读。

面向对象设计原则的应用
在两次作业的迭代过程中,我逐渐体会到了面向对象设计原则的重要性。以下是我在设计过程中应用的主要设计原则:
单一职责原则(SRP):每个类只负责一项职责。例如,配送类负责管理配送订单和费率计算,支付类负责处理支付逻辑,货物类负责处理货物信息和体积重量计算。
开闭原则(OCP):软件实体应该对扩展开放,对修改关闭。通过使用抽象类和枚举类型,系统可以在不修改现有代码的情况下添加新的支付方式和货物类型。
里氏替换原则(LSP):子类可以替换父类并且不会破坏程序原有功能。具体支付类继承自抽象支付类,能够正确替换父类对象而不影响程序逻辑。
接口隔离原则(ISP):客户端不应该依赖它不需要的接口。虽然在这个系统中接口的使用较少,但每个类的方法都是必要的,没有多余的接口。
依赖倒置原则(DIP):高层模块不应该依赖低层模块,两者都应该依赖于抽象。支付模块依赖于抽象的支付类,而不是具体的支付实现,提高了系统的灵活性。

两次代码分析(hang文件为第一次作业;Main文件为第二次作业):

整体项目度量数据(hang.java 和 Main.java 的度量数据列表截图相关指标)
Lines(行数):hang.java 为 378 行,Main.java 为 495 行 。行数在一定程度上反映了文件的规模,较多的行数可能意味着代码逻辑复杂,维护难度较高。不过单纯的行数多并不一定代表代码质量差,还需结合其他指标综合判断。
Statements(语句数):hang.java 有 188 条语句,Main.java 有 258 条语句 。语句数反映了代码中实际执行操作的数量,较多的语句数可能暗示着复杂的逻辑。
% Branches(分支语句占比):hang.java 为 6.4%,Main.java 为 10.9% 。分支语句(如 if - else、switch 等)占比越高,说明代码的逻辑分支越多,代码的复杂度也就越高。
Calls(方法调用数):hang.java 是 55 次,Main.java 是 75 次 。方法调用数体现了代码的模块间交互情况,较多的调用数可能意味着代码的耦合度较高。
% Comments(注释行占比):hang.java 为 14.0%,Main.java 为 12.3% 。注释有助于理解代码逻辑,适当的注释占比可以提高代码的可读性。但这里的占比并不是越高越好,如果注释过多可能意味着代码本身不够清晰易懂。
Classes(类数量):hang.java 有 3 个类,Main.java 有 6 个类 。类数量反映了代码的模块化程度,合理的类划分有助于提高代码的可维护性。
Methods/Class(平均每个类的方法数):hang.java 为 7.67,Main.java 为 5.83 。平均每个类的方法数体现了类的职责丰富程度,如果某个类的方法数过多,可能意味着该类违反了单一职责原则。
Avg Stmts/Method(平均每个方法的语句数):hang.java 是 7.57,Main.java 是 7.20 。平均每个方法的语句数可以帮助判断方法的复杂程度,较多的语句数可能意味着方法做了过多的事情,需要考虑拆分。
Max Complexity(最大复杂度):hang.java 和 Main.java 均为 2 。复杂度通常基于代码的分支、循环等结构计算,最大复杂度反映了代码中最复杂方法的复杂程度。
Max Depth(最大嵌套深度):hang.java 和 Main.java 均为 3 。最大嵌套深度体现了代码中控制结构(如 if - else、for 循环等)的嵌套层数,嵌套层数过高会使代码难以理解和调试。
Avg Depth(平均嵌套深度):hang.java 为 0.84,Main.java 为 1.07 。平均嵌套深度从整体上反映了代码的嵌套情况。
Avg Complexity(平均复杂度):hang.java 为 2.00,Main.java 为 1.11 。平均复杂度综合反映了代码中方法的复杂程度。
具体文件度量详情(Main.java 和 hang.java 的度量详情截图相关指标)
以 Main.java 为例,除了上述整体项目度量数据中包含的指标外,还能看到一些具体信息:
Line Number of Most Complex Method(最复杂方法所在行号):Main.java 中最复杂方法在 329 行,是 Main.main () 方法 。这提示我们需要重点关注该方法的逻辑,看是否可以进行优化拆分。
Name of Most Complex Method(最复杂方法名称):明确最复杂的方法是 Main.main () ,结合其复杂度为 2,语句数较多等情况,可能需要对该方法进行重构,使其职责更加单一。
Most Complex Methods in 4 Class (es)(4 个类中最复杂的方法):列出了多个类中较复杂的方法及其复杂度、语句数、最大深度和调用数等信息。例如 AliPay.AliPay () 等方法,通过这些信息可以对代码中复杂的部分有更清晰的认识,有针对性地进行优化。
Block Depth vs Statements(嵌套深度与语句数关系):从图表中可以直观看到不同嵌套深度下的语句分布情况,帮助我们了解代码的结构是否合理。比如在 Main.java 中,嵌套深度为 0、1、2 时语句数相对较多,说明代码的主要逻辑集中在这些层次。
基于度量数据的代码质量分析
复杂度方面
从最大复杂度指标来看,hang.java 和 Main.java 的最大复杂度均为 2,相对较低,说明代码中没有极其复杂难以理解的方法。但从平均每个方法的语句数(hang.java 为 7.57,Main.java 为 7.20 )以及方法调用数等指标来看,代码仍然有一定的复杂度。尤其是 Main.main () 方法较为复杂,作为程序入口,它承担了较多的逻辑处理,这与之前分析的主类职责过重问题相呼应,需要进一步拆分逻辑,降低方法复杂度。
可读性与可维护性方面
注释行占比 hang.java 为 14.0%,Main.java 为 12.3% ,处于较为合理的范围,对代码的理解有一定帮助。但结合代码行数和逻辑复杂度,如果能进一步优化注释,针对关键逻辑和复杂算法进行更详细的说明,将更有利于提高代码的可读性和可维护性。同时,类数量和每个类的方法数分布情况表明,代码在模块化和单一职责方面还有提升空间,部分类的方法数较多,可能需要进一步拆分职责,使每个类的功能更加单一明确。
耦合度方面
从方法调用数等指标可以看出,代码中存在一定的模块间交互。结合之前设计分析中提到的支付类与配送类耦合度问题,虽然在第二次作业中进行了优化,但仍需持续关注方法调用关系,避免过度耦合,进一步提高代码的可维护性和扩展性。

三、采坑心得
1.耦合度问题:支付类与配送类的紧耦合是前期设计的一大隐患。支付类直接调用配送类的费率计算逻辑,导致两者相互依赖。这使得代码维护时,一处修改可能引发连锁反应,牵一发而动全身。比如,当配送类的费率规则调整,支付类也得跟着改,增加了出错风险和维护成本。解决此问题时,在抽象支付类中引入配送类引用,让支付类通过配送类接口获取费率计算结果,降低了耦合度,使两个模块可独立开发、测试和维护 。
2.扩展性问题:早期未使用枚举类型,对固定类型信息采用字符串或条件判断处理,极大限制了系统扩展性。新增支付方式或货物类型时,需在多处代码中修改条件判断逻辑,不仅工作量大,还容易遗漏,导致系统出现漏洞。引入枚举类型后,新增类型只需在枚举定义中添加常量,再创建相应实现类,其他代码无需改动,有效提升了系统扩展性和可维护性 。
3.代码重复问题:支付类和配送类中重复的费率计算逻辑,是代码冗余的典型表现。重复代码不仅增加了代码量,更使得维护难度翻倍,修改费率规则时需同时更新多处代码,极易出错。通过将费率计算逻辑统一整合到配送类,支付类调用配送类的方法来计算费用,消除了重复代码,确保规则修改时只需维护一处代码,提高了代码的一致性和可维护性 。
4.主类臃肿问题:最初主类承担了用户输入处理、业务逻辑处理、折扣计算、结果输出等众多职责,代码冗长复杂,可读性和可维护性极差。一旦需求变更,修改主类代码容易引发新问题。优化时,将主类中部分功能抽取为辅助方法,如折扣计算、支付方式名称转换等,使主方法逻辑更清晰,职责更单一,增强了代码的可读性和可维护性 。
5.代码度量与质量分析:借助 SourceMonitor 工具分析代码度量数据后,发现代码在复杂度、可读性与可维护性、耦合度等方面虽有一定优点,但仍存在不足。比如 Main.main () 方法复杂度较高,主类职责过重问题在代码度量数据中得到进一步验证。这提醒我们在开发过程中,不能仅靠经验判断代码质量,需借助专业工具进行量化分析,及时发现潜在问题并优化 。
四、改进建议
改进建议
1.深入应用设计模式:进一步引入策略模式来优化支付方式和费率计算。将支付方式和费率计算分别抽象为独立的策略接口,根据不同情况选择具体策略实现。这样能使系统更加灵活,当有新的支付方式或费率规则时,只需添加新的策略实现类,而无需修改现有代码,提高系统的可扩展性和可维护性。
2.持续优化代码结构:对代码进行全面的代码审查,检查是否还存在职责不清晰、耦合度过高的类和方法。根据审查结果,进一步拆分职责,降低类与类之间的耦合度,确保每个类都遵循单一职责原则。
3.加强注释和编写:虽然代码中已有一定比例的注释,但对于关键算法和复杂逻辑部分,进一步添加详细注释,提高代码可读性。同时,编写系统设计文档,记录系统架构、模块功能、接口定义等信息,方便团队成员理解和维护代码,也便于后续新成员快速上手。
五、总结
通过这次物流管理系统的迭代开发,我在面向对象设计和实践方面有了很大的收获。从最初设计中存在的耦合度高、扩展性差等问题,到通过引入枚举类型、优化模块设计等方式逐步改进,我深刻体会到遵循面向对象设计原则的重要性。单一职责、开闭等原则能有效提高代码的质量和可维护性。
在开发过程中,遇到的种种问题让我认识到软件开发是一个不断迭代优化的过程。不能在完成基本功能后就止步不前,而要持续关注代码质量,及时发现并解决潜在问题。同时,合理运用工具如 SourceMonitor 进行代码度量分析,能为代码优化提供有力依据。
这次经历也为我今后的软件开发工作积累了宝贵经验。在未来的项目中,我会更加注重设计的前瞻性和扩展性,在项目初期就合理规划代码结构,遵循设计原则。并且会更加积极地使用工具进行代码审查和度量,不断提升代码质量,打造出更健壮、高效、可维护的软件系统。

posted @ 2025-05-25 18:24  七677  阅读(18)  评论(0)    收藏  举报