航空运输大作业分析
前言:
这两次题目集重点关注了继承与多态,通过对之前的题目进行重构来让学生了解继承与多态(以及开闭原则等原则),在遵循原则后可使迭代进行地更加便捷。因为是迭代,题目的题量在源码的衬托下并不显得多,而且对于上次大作业来说,本次题目并不算难,因为没有什么太复杂的逻辑,都是日常生活中常见的方法。
设计与分析
(1)第一次源码:
}

代码规模相关指标
- 文件数(Files):1 个,表明代码集中在单个文件中,便于管理和维护,但如果文件过大可能带来可读性等问题。
- 行数(Lines):352 行,不算特别长,但也有一定规模。行数较多时,需要关注代码结构和组织,避免冗长和混乱。
- 语句数(Statements):222 条,反映代码实际执行操作的数量。
代码结构相关指标
- 分支语句百分比(Percent Branch Statements):4.1% ,分支语句占比较低,说明代码逻辑相对简单,程序流程的分支跳转情况不复杂。
- 方法调用语句数(Method Call Statements):54 条,表明代码中存在较多方法间的调用,反映了代码的模块化程度有一定基础。
- 含注释行百分比(Percent Lines with Comments):3.7% ,注释比例较低,可能影响代码可读性和后期维护理解,后续应适当增加注释。
- 类和接口数(Classes and Interfaces):6 个,代码具有一定的面向对象结构。
- 每个类的方法数(Methods per Class):7.67 ,平均每个类拥有的方法数量较多,可能意味着类的职责不够单一,可考虑进一步拆分优化。
- 每个方法的平均语句数(Average Statements per Method):3.02 ,方法内语句数较少,说明方法粒度较细,这是比较好的编程习惯,利于代码的复用和维护。
代码复杂度相关指标
- 最复杂方法行号(Line Number of Most Complex Method ):未定义,最复杂方法名称(Name of Most Complex Method )为
Cargo.getRate(),最大复杂度(Maximum Complexity )为 7 ,说明Cargo.getRate()方法相对复杂,可对其进行重构优化。 - 最深代码块行号(Line Number of Deepest Block ):未定义,最大代码块深度(Maximum Block Depth )为 3 ,平均代码块深度(Average Block Depth )为 1.67 ,平均复杂度(Average Complexity )为 1.24 ,整体复杂度不算特别高,但仍有优化空间。
可视化图表
- Kiviat 图:展示了代码在注释比例、方法/类数量、平均复杂度、平均深度、最大深度、最大复杂度等维度的情况,直观呈现各指标间的相对关系。
- Block Histogram:从直方图看,语句数量在 2 的区间内占比最高,反映了代码语句分布情况,辅助理解代码结构。
![]()
复杂度(Complexity)
- Cargo.getRate() 复杂度为7,在这些方法中最高,意味着其逻辑相对复杂,可能包含较多条件判断、循环嵌套或方法调用等复杂结构,是代码优化重点关注对象。
- 其余方法如 Customer.setAddress()、Flight.setMaxWeight()、ShippingInfo.getFlightNumber() 复杂度为1,逻辑相对简单。GoodsList.show() 复杂度为2 ,Main.main() 复杂度为3 ,复杂程度适中。
语句数(Statements)
- Main.main() 语句数达40,是所有方法中最多的,说明该方法执行的操作较多,代码量较大。
- GoodsList.show() 有15条语句,也有一定代码量。而 Customer.setAddress()、Flight.setMaxWeight()、ShippingInfo.getFlightNumber() 都只有1条语句,功能较为单一。Cargo.getRate() 有11条语句 ,语句数量适中。
最大深度(Max Depth)
- Cargo.getRate()、GoodsList.show()、Main.main() 最大深度均为3,表明这几个方法内代码块(如循环、条件语句等)嵌套层次相对较深,理解和调试难度较大。
- Customer.setAddress()、Flight.setMaxWeight()、ShippingInfo.getFlightNumber() 最大深度为2 ,嵌套层次相对浅一些。
方法调用数(Calls)
- Main.main() 方法调用数为34,GoodsList.show() 为14 ,说明这两个方法会调用较多其他方法来实现功能,体现其功能的综合性和对其他模块的依赖程度高。
- Cargo.getRate() 方法调用数为1 ,而 Customer.setAddress()、Flight.setMaxWeight()、ShippingInfo.getFlightNumber() 方法调用数为0 ,表明这些方法相对独立,较少依赖其他方法。
![]()
代码块深度(Block Depth)与语句数(Statements)分析
-
数据分布:
- 深度0(无嵌套):9条语句,占比约4%,多为顶层代码(如类/方法定义、简单赋值等)。
- 深度1(单层嵌套,如
if或for内的代码):74条语句,占比约33%,是常见的逻辑嵌套层次。 - 深度2(双层嵌套,如
if内嵌套for或else内嵌套if):120条语句,占比约54%,为代码的主要组成部分,反映核心逻辑的嵌套复杂度。 - 深度3(三层嵌套):19条语句,占比约8.5%,存在少量复杂逻辑,需关注优化。
- 深度4及以上:0条语句,无过深嵌套,避免了极高复杂度的代码结构。
-
复杂度与可读性:
- 优势:
- 嵌套深度集中在1-2层(占比87%),整体结构清晰,降低了理解和维护难度。
- 无深度≥4的嵌套,避免了“嵌套地狱”问题,代码逻辑可追溯性强。
- 优化点:
- 深度3的19条语句:检查是否存在不必要的三层嵌套(如多重条件判断或循环嵌套),可通过提取子方法、简化逻辑(如合并条件、使用设计模式)降低嵌套深度。
- 深度2的高占比:虽为合理嵌套,但需确保双层逻辑的可读性(如添加注释、拆分复杂逻辑),避免因代码量多导致的维护成本上升。
- 优势:
-
语句分布合理性:
总语句数222条,与之前的代码分析数据一致。各深度语句呈“中间高、两端低”分布,符合正常代码逻辑(核心逻辑多在1-2层嵌套中实现,简单/复杂逻辑占比少),整体结构均衡。 -
对代码质量的影响:
- 可读性:多数代码(87%)在1-2层嵌套内,便于快速理解逻辑;少量三层嵌套需局部优化。
- 可维护性:无过深嵌套,降低了修改风险;深度2的高占比需确保代码模块化(如将复杂双层逻辑封装为方法),提升扩展性。
- 性能:嵌套深度与性能无直接关联,但合理的嵌套结构(避免冗余)可间接提升执行效率。
总结:此次 作业代码嵌套深度低,虽然在注释量上有不足,但简单的结构和意义明确的属性名和方法名让我能在迭代时畅通无阻地进行增加和修改,不足之处是没有尽早使用抽象类和接口,在迭代时使用了交多少时间来进行抽象类的设置和子类的添加。
(2)第二次源码:


代码指标深度分析
-
代码规模与结构:
- 文件与行数:单文件(1个)包含473行代码,语句数162,规模适中。需注意单文件过大可能影响导航,若逻辑复杂可考虑按模块拆分。
- 分支与调用:分支语句占比7.4%(低),说明逻辑流程简单(如少用
if-else、switch等);方法调用仅17次,模块化不足,可通过提取工具类、公共方法提升复用(如重复的计算逻辑、数据处理等)。 - 注释与类结构:注释占比1.5%(严重不足),需在类定义、复杂方法(如
Dangerous.getRate())、关键逻辑处添加注释(如业务规则、算法说明)。11个类和接口,平均每个类4.45个方法,类职责需检查(如是否存在“上帝类”处理过多功能,可通过UML类图分析依赖关系)。
-
复杂度与可读性:
- 方法复杂度:最复杂方法
Dangerous.getRate()复杂度7(中等),需审查其逻辑(如是否有多重嵌套条件、循环),可通过“提取方法”重构(将子逻辑拆分为私有方法)。平均方法语句数1.67(极细粒度),虽利于维护,但需避免过度碎片化(如无意义的单行方法,可合并相关操作)。 - 代码块深度:最大深度3(浅嵌套),平均深度1.48,说明代码结构扁平(如少用
for嵌套、多层if),可读性佳。直方图显示深度2语句占比最高(核心逻辑区),深度3仅少量(需局部优化,如简化三层嵌套为两层)。
- 方法复杂度:最复杂方法
-
可视化分析:
- Kiviat图:注释维度(1.5%)显著低于其他指标,是优化优先级;复杂度、深度维度分布均衡,反映代码整体“低复杂度、浅嵌套”的健康状态。可通过雷达图对比历史版本,监控注释率、复杂度等指标的变化趋势。
- 块直方图:语句集中在深度0-3(无深度≥4),避免了“嵌套地狱”,但深度2的高占比需确保代码模块化(如将双层逻辑封装为服务类方法),提升可测试性(如单元测试更易覆盖)。

复杂方法分析
-
高复杂度方法(
getRate()系列):- 共性:
Cargo.getRate()、Dangerous.getRate()、Expedite.getRate()均为费率计算方法,复杂度7(中等偏高),语句数11,最大深度3(三层嵌套,如条件判断或循环),调用数1(依赖少量外部方法)。- 问题:逻辑重复(如费率计算规则相似),嵌套深导致可读性差,维护成本高。
- 优化:
- 抽象公共费率计算逻辑到
RateCalculator工具类,通过参数(如货物类型、重量)统一处理,减少重复代码。 - 拆分三层嵌套为
calculateBaseRate()、applyDiscount()等子方法,降低单个方法复杂度(目标≤5),提升可读性。
- 抽象公共费率计算逻辑到
- 共性:
-
低复杂度方法(构造与简单方法):
- 共性:
Corporate.Corporate()、Customer.setAddress()等方法复杂度1,语句数1,多为初始化或简单赋值,嵌套深度2(可能含单层校验,如参数非空检查)。- 问题:构造方法可能存在重复初始化(如
Corporate、Individual、Normal的构造逻辑),setAddress()缺少数据校验(如地址格式合法性)。 - 优化:
- 提取公共初始化逻辑到基类(如
Entity),通过继承减少重复代码。 - 在
setAddress()中添加正则校验(如地址格式检查),抛出异常增强健壮性,避免无效数据。
- 提取公共初始化逻辑到基类(如
- 问题:构造方法可能存在重复初始化(如
- 共性:
-
类与方法分布:
- 费率计算类(3个):
Cargo、Dangerous、Expedite集中处理费率,可通过接口(RateCalculatable)抽象,实现多态调用(如rateCalculator.calculate()),解耦业务逻辑与具体实现。 - 辅助类(5个):
Corporate、Customer等管理数据,需确保与费率逻辑解耦(如通过依赖注入传递RateCalculator,而非直接耦合),提升可测试性。
- 费率计算类(3个):
-
调用数与深度:
- 调用数1:高复杂度方法依赖少量外部方法,说明内部计算逻辑为主,可通过单元测试覆盖所有费率分支(如不同货物类型、重量区间),确保逻辑正确。
- 深度3:三层嵌套需重点优化(如用策略模式替换多重
if-else,每个策略处理单一费率规则),使代码更易扩展(新增费率规则只需添加策略类)。
![]()
代码块深度与语句分布分析
-
数据分布:
- 深度0(无嵌套):16条语句,占比约8.4%,多为顶层代码,结构简洁。
- 深度1(单层嵌套):65条语句,占比39.4%,常见逻辑层,可读性较好。
- 深度2(双层嵌套):72条语句,占比43.6%,核心业务逻辑区,需关注模块化。
- 深度3(三层嵌套):12条语句,占比7.3%,存在少量复杂逻辑,需优化。
- 深度4+:0条,无过深嵌套,避免高复杂度风险。
-
复杂度优化:
- 深度3处理:检查三层嵌套逻辑(如多重条件或循环),通过提取子方法(如
handleComplexLogic())或合并条件(减少嵌套层级),将深度降至2层,提升可读性。 - 深度2增强:对双层嵌套的核心逻辑(如业务规则实现),封装为独立方法(如
processBusinessRule()),增强复用性和可测试性,减少重复代码。
- 深度3处理:检查三层嵌套逻辑(如多重条件或循环),通过提取子方法(如
-
可读性与维护性:
- 注释补充:为深度2和3的代码块添加注释(如业务规则、算法说明),明确嵌套逻辑的目的(如“此处验证用户权限后执行操作”)。
- 结构优化:确保深度0的顶层代码(如类/方法定义)职责单一,避免在
main或构造方法中编写复杂逻辑,通过依赖注入或服务委托分离关注点。
总结:代码结构简单没有过度复杂的语句,设置了迭代可能遇到的方法和抽象类,接口(接口的第一次尝试)。
踩坑心得:
1.大作业相当于其他小练习代码比较长,这就让我们在出现错误时尤其要注意处理逻辑的区域是否出现错误,在首次大作业中
![]()
此处代码逻辑出现错误(在rate的设置上)但是测试样例是正确的,因为测试样例使用的正好是逻辑正确的那段代码。
![]()
在答案错误上查找了三个小时,最后发现这里错误也是把我给气笑了:),在查找的过程中总是以为是输出格式的错误,而逻辑部分基本是一笔带过,结果问题还是在逻辑上;),这告诉我不能手高眼低偏执的过度关注一个地方,不管三七二十一就一股脑的往一个方向思考。
2.在第二次大作业中有一处使用switch![]()
导致出现了非零返回,对于switch使用出现了问题,也不知道如何处理,自己认为逻辑正确,但是仍然非零返回,使用豆包帮忙后仍然不正确,最后还是回归最经典的if-else并且解决了问题![]()
虽然尝试是好事,但返璞归真也能解决很多问题,有时候就是我们自作聪明导致越做越烂。
改进建议:
![]()
在第一次大作业中没有养成以上来就写抽象类的习惯,导致后面的作业花了更长时间来满足题目要求,第一次先打好地基才能让后续程序搭建变得既方便又牢固,让程序更合理,更容易。从实质上来说就是要满足程序的开闭原则,方便后续的拓展。
总结:
在本次大作业中,我学系到了不能眼高手低地做题量大的大作业,理解了为什么开闭原则老师每次都要求我们去遵循,以及不能在做题时去自作聪明地尝试自己并不是非常了解的食物,这些东西大可在平时自行学习。









浙公网安备 33010602011771号