前言
这两次题目集让我学会了很多:
1:我学会了多态与继承,我明白了abstract(抽象类),同时,我学会了“@Override”,以及在子类中对父类进行改写,通过“super()”语句;
2:只要在 nextInt()、nextDouble() 等方法之后,需要立即使用 nextLine() 读取字符串,就必须先调用 scanner.nextLine() 清除残留换行符,确保输入逻辑正确。一开始,我的代码的运行总会非0返回,这个点卡了我许久,知道后来我才知道是输入格式的问题,除此之外,还可以用Double.parseDouble(scanner.nextLine())的输入方法,也可以改变输入。
对这两次题目集难度的评估:
这两次题目集难度并不大,主要是使用了大量的多态与继承,在算法方面,这两次的题目并不像第一次作业有很高级的运行规则(上一次的题目集电梯运行规则一开始过于复杂,导致一开始一直在考虑算法的问题),这次作业的难度主要在于有大量的数据需要输入,输出,处理,只需要仔细把握好各个数据点之间的关系,便可轻松作答(主要是确定父类与子类,以及子类如product中确定物品的计费重量,以及物品的费率,和客户种类的打折)。
设计与分析
题目集八
首先,要确定这道题一开始的输入数据
客户编号
客户姓名
客户电话
客户地址
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为YYYY-MM-DD)
航班最大载重量
订单编号
订单日期(格式为YYYY-MM-DD)
发件人地址
发件人姓名
发件人电话
收件人地址
收件人姓名
收件人电话
从输入的数据中可以找到类的设计,首先是Customer类,Flight类,Payment类,WeChatPay类作为Payment类的子类,Product类,Order类,然后,由于Sender,Receiver两个类过于相似,我就设计了一个他们的父类Contact,当然,其实不需要这样设计,现在想想反而更复杂,也不符合多态与继承的设计。
然后给下来是这次实验的类图,
类图的分析
Customer 类与 Order 类关联,一个订单对应一个客户。
Order 类与 Flight 类关联,一个订单对应一趟航班。
Order 类与 Payment 类关联,一个订单对应一种支付方式。
Order 类与 Product 类是聚合关系(0..* 表示一个订单包含多个产品) 。
Order 类与 Sender 类、Receiver 类关联,分别表示订单的寄件人和收件人。
WeChatPay 继承自 Payment,实现支付的具体方式。
Sender 和 Receiver 继承自 Contact,获取通用联系信息属性和方法。
主要类Order 类
属性:orderId(订单编号)、orderDate(订单日期)、customer(客户)、sender(寄件人)、receiver(收件人)、products(产品列表)、flight(航班)、payment(支付方式) 。
方法:构造方法;初始化订单(initializeOrder)、计算总运费(calculateTotalFreight)、总重量(calculateTotalWeight )、检查载重(checkLoadCapacity)、打印订单详情(printOrderDetails)等方法 。是系统的核心类,用于管理订单相关业务逻辑。
复杂度分析
主文件为 “Main.java”,代码总行数 323 行,语句总数 161 条,分支语句占比 5.0%,方法调用语句 40 条,含注释的行占比极低,仅 0.9%,类和接口共 9 个。由此可见,代码规模较为常规,但注释稀缺,对代码的可读性与后期维护埋下隐忧。好的注释能够方便下一次的阅读,然而我的注释较为稀少,可能对我自己当前的阅读没有影响,然而对以后的阅读以及修改可能造成不方便,因此要添加更多的注释。
第二组表格呈现代码的复杂度与方法分布。平均每个类的方法数为 3.67 个,每个方法平均语句数 2.70 条。其中,Product.calculateRate() 方法最为复杂,行数达 131 行,最大复杂度为 5,最深代码块行数 111 行,最大代码块深度 3,平均代码块深度 1.35,平均复杂度 1.17。这表明整体代码复杂度尚可,但存在个别较复杂的方法,可进一步探究其逻辑细节与优化空间。
第三组表格与柱状图相互呼应,代码块深度为 0 时,有 14 条语句;深度为 1 时,有 81 条;深度为 2 时,有 61 条;深度为 3 时,有 5 条,更深层次则无语句,我认为我的代码写的较为合理,因为没有使用较难的语句。柱状图中,深度为 1 的语句数量如高峰耸立,随着深度增加,语句数量逐渐递减,恰似代码逻辑由浅入深的涟漪,层次分明,规律尽显。雷达图则如精密的多维探测器,展示了含注释行的百分比、每个类的方法数、平均复杂度、平均代码块深度、最大代码块深度等指标,勾勒出代码质量的多面轮廓。尽管注释极少,但从整体结构与复杂度来看,仍有可圈可点之处,若能在注释方面加以完善,代码的清晰度与可维护性将更上一层楼。
当然在这一次的习题集中,我也认识到了自己的不足,比如:我在继承和多态方面有一定的不足,不能较为轻松的使用super()语句,同时我也不太分得清什么时候要使用abstract。
而现在我知道了抽象方法具有以下特点:
仅具备方法的声明,却没有具体的实现代码,方法声明后直接以分号结尾。
必须在抽象类中进行定义。
子类继承该抽象类后,通常需要对抽象方法进行重写,不过存在特殊情况。
例如
会使用abstract。
题目集九
在这次题目集当中,额外添加了客户的类型Individual/Corporate,用户分为个人用户和集团用户,其中个人用户可享受订单运费的9折优惠,集团用户可享受订单运费的8折优惠
同时增加了付费的方式可输入项:Wechat/ALiPay/Cash
以及增加了货物的种类Normal/Expedite/Dangerous(每一个种类都有不同的费率计算方法)
在这三个方面都可以使用继承与多态。
下面是这一次的类图
这个类图的优点在于清晰的类层次结构和职责划分,便于理解和维护。如客户、产品、订单等不同模块独立又相互协作。
利用抽象类定义通用行为和属性,为子类提供规范,利于代码复用和扩展,如Customer、Payment、Contact抽象类。
当然了,这个类图依然有所不足
缺少对系统性能优化相关设计,如大量订单处理时的效率问题。
Customer:抽象类,定义客户的通用属性(customerId、name、phone、address、customerType )和方法(获取属性的方法 )。
Corporate:Customer的子类,代表企业客户,构造函数接收客户相关信息。
Individual:Customer的子类,代表个人客户,构造函数接收客户相关信息。
Normal、Dangerous、Expedite:Individual的子类,分别代表普通、危险、加急类型的个人客户,都有calculateRate方法用于计算费率,且有各自的构造函数接收产品
相关尺寸、重量等信息。
Product:描述产品信息,属性有productid、productname、width、length、height、weight、rate、money ,包含设置和获取属性方法,以及计算运费相关方法(如calculateFreight )。
Payment:抽象类,定义支付行为pay方法。
AliPay、WeChatPay、Cash:Payment的子类,分别实现具体的支付方式,重写pay方法。
相比于上次的题目,我在上次的基础上,除了增添了几个子类之外,我还重写了计算费率的算法calculateRate(),以及通过customer.getCustomerType().equals("Individual")
,判断因该打几折。
复杂度分析
从这些数据和图表来看,这份代码有其优势,但也存在明显不足。整体代码规模不算庞大,复杂度也处于相对较低水平,这是比较好的基础。不过一些关键指标表现欠佳,像注
释占比极低,这对代码长期维护很不利。
行数和语句数:406 行对应 215 条语句,说明代码行的 “有效密度” 不是特别高,可能存在一些空行或者一行多语句的情况。语句数不算多,意味着整体功能逻辑在语句层面
不是特别繁杂。方法调用语句数:有 57 条方法调用语句,表明代码中方法之间的交互比较频繁,体现了一定的模块化设计思路,不同功能通过方法调用协作实现。
类和接口数量:16 个类和接口,反映出代码在结构上有一定的复杂性,不同的类型可能对应不同的功能模块或抽象概念,这要求开发者对整体架构有清晰把握。
每个类的平均方法数:平均每个类 2.69 个方法,说明类的功能划分不算特别精细,可能存在单个类承担过多职责的情况,不符合单一职责原则,后续扩展或修改时可能引发连
锁反应。
每个方法的平均语句数:平均每个方法 3.16 条语句,方法短小精悍,这是很理想的状态,短小的方法更容易理解、测试和复用。
最复杂方法:Dangerous.calculateRate() 方法在第 199 行,这个方法是代码中的 “风险点”。它可能由于业务逻辑复杂,或者代码编写不够优化,导致复杂度较高,后期维
护时需要格外小心,必要时可以考虑拆分重构。
最大和平均代码块深度:最大深度 3 ,平均深度 1.35 ,说明代码的嵌套层次较浅,代码结构比较扁平,这对于代码阅读和理解非常友好,降低了逻辑的嵌套难度。
平均复杂度 1.24:整体复杂度低,表明代码在设计和实现上没有过度复杂的逻辑交织,从复杂度角度来说代码质量尚可。
这份代码基础还可以,简单的结构和较低的复杂度是优势。但注释严重不足和部分结构设计不够合理(如类的职责划分)是明显短板。后续维护中,首先要大量补充注释,让代码
逻辑清晰可懂;对于复杂方法考虑重构优化;同时持续监控代码复杂度,防止因新功能添加导致复杂度失控。
采坑心得
这一系列题目我的错误并不多
1:非0返回
这段代码出现了非0返回的问题
(主要出错为输入格式的不正确),然后我询问了AI才知道错误出现在在 nextInt()、nextDouble()等方法之后,需要立即使用 nextLine() 读取字符串,当然
也可以使用Double.parseDouble(scanner.nextLine())和Integer.parseInt(scanner.nextLine())替代,当然我认为以后输入数字,不管后面有无scanner.nextLine(),
都可以用Double.parseDouble(scanner.nextLine())和Integer.parseInt(scanner.nextLine()),这样子就可以不用担心输入格式的问题了。其实这次的期中考试我也碰到
了这个问题,当时也在这里处理错误,导致非0返回,(忘了处理方法)。
2:输入的大小写问题
这里一开始我以为是我算法出现了问题,然后我一直在计算物品费用的地方查找错误,却一直没有找出来,然后我叫AI给我依据题目给我生成了几个测试用例,最
后发现是Wechat输入比较有问题
我是对WeChat比较,而实际上是与Wechat比较。因此我发现运用AI生成大量的测试用例可以方便我们更轻松地找到自己的错误。
改进建议
这道题目实际上还有许多地方可以深度优化
1:首先,这道题目中,无论输入多少件货物,实际上它们只能是一个种类(即Normal/Expedite/Dangerous)但实际上,因该一次性输入多个不同种类的物品,
这个地方可以迭代优化。
2:多航班组合运输:当一个航班无法承载全部货物时,允许系统自动规划多个航班组合运输方案,计算不同组合的总运费和运输时长,供用户选择。此时需增加航班组合逻辑以及对多航班运输费用、重量等的综合计算逻辑。
3:旺季 / 淡季差异化策略:根据不同季节(如节假日前后等货运旺季)设置不同的运费调整系数、折扣策略等。在系统中增加时间判断逻辑,根据当前日期判断是否处于特殊时段并应用相应策略。
总结
本题围绕某航空公司 “航空货运管理系统” 展开,主要设计了空运费计算及相关订单处理程序设计,我们在这道题目中使用了大量的继承与多态。
从业务逻辑看,核心是空运费计算。首先确定计费的重量,通过比较货物实际重量与体积重量(通过货物体积(长宽高)除以 6000 计算),取较高者。接着依据货物类型(普通、危险、加急)确定费率,结合用户类型(个人 9 折、集团 8 折)算出基础运费。程序需模拟客户货运业务,接收客户、货物、航班、订单等多方面信息输入,包括了发件人身份,收件人身份、货物规格、航班详情、订单支付的价格和货物的基本信息(价格,重量,费率)等。
题目在功能实现上要求严格。输入有明确格式规范,按特定顺序输入各类信息;输出根据航班载重量判断,超限时提示并终止,未超限时输出详细订单信息报表和货物明细报表,包括客户信息、订单关键信息、支付金额及货物明细(计费重量、费率、运费等),且实型数保留一位小数。
这道题目的考察点主要在于:单一职责原则、里氏代换原则、开闭原则、合成复用原则、依赖倒转原则。通过继承与多态,我对大量重复的方法进行改写与复用,如此一来可以大大减少所需工作量并且加快代码的运行速度。
同时,我还了解了基本的输入格式,nextInt(),nextDouble()后接nextLine()必须在加一个nextLine()从而不会非返回,或者使用Double.parseDouble(scanner.nextLine())也是可行的。
总而言之,我在这道题目中收获良多,学会了继承与多态中的改写与复用,学会了使用AI生成用例以加快查找错误的地方。