第二次Blog作业
一、前言
此次迭代的题目集为航空货运管理系统,两次题目集分别考察了类设计和继承与多态。第一轮迭代主要考察了面向对象设计原则,具体为单一职责原则、里氏替换原则、开闭原则和合成复用原则。其中单一职责原则中,每个类专注于单一功能;里氏替换原则中,子类可替换父类,如不同费率策略实现同一接口;开闭原则中,通过策略模式扩展新费率区间;合成复用原则中,通过组合(而非继承实现功能复用。题量中等,需要实现完整的订单处理流程,其中包含了许多板块。难度较高,要满足多个设计原则和功能需求。第二轮迭代主要考察了更深层次的面向对象设计原则,具体为新增了依赖倒转原则、策略模式,依旧的开闭原则和单一职责原则。其中依赖倒转原则中,依赖抽象,而非具体实现;策略模式中,通过不同策略类实现货物类型费率、用户折扣的灵活组合;开闭原则中,通过接口扩展支付方式(新增现金支付)、用户类型;单一职责原则中,拆分为Goods、RateCalculator、DiscountService类。题量较高,新增货物类型、用户折扣逻辑,支付方式扩展,设计原则复杂度提升。难度也较高,要同时满足复杂的计费逻辑,严格遵循设计原则以及各类的灵活扩展。
二、设计与分析
题目集八
第一次代码虽然没有在规定时间内取得满分,但是在之后的不断修改中取得了满分。
类设计
具体分析
- 代码结构与类设计
代码采用了面向对象的设计,包含以下主要类:
- Customer:存储客户基本信息(ID、姓名、电话、地址)。
- Cargo:表示货物,计算体积重量和计费重量。
- Flight:管理航班信息,包括载重检查。
- Order:处理订单逻辑,计算总运费。
- Main:程序入口,负责用户输入和输出。
- 关键业务逻辑
- 货物重量计算
体积重量:通过公式 (长×宽×高)/6000 计算(单位:kg)。
计费重量:实际重量和体积重量中的较大值。 - 运费计算
根据计费重量分档计算单价:
< 20kg:35 元 /kg
20-49.9kg:30 元 /kg
50-99.9kg:25 元 /kg
≥100kg:15 元 /kg
总运费 = 各货物计费重量 × 对应单价之和。
- 输入与输出
- 输入流程
读取客户信息
读取货物信息
读取航班信息
读取订单信息 - 输出内容
订单基本信息(客户、航班、日期、收发件人等)
总重量和支付金额
货物明细(编号、名称、计费重量、费率、运费)
SourceMonitor图
![]()
- 代码复杂度分析
-
最高复杂度方法:Order.calculateRate()(复杂度为5),包含多层条件分支(4个if-else)。
问题:费率计算逻辑硬编码,违反开闭原则,新增货物类型需直接修改方法。
-
平均复杂度:1.44,整体逻辑简单,但Order类承担过多职责。
问题:Order类既管理货物列表,又处理费率和总运费计算,违反单一职责原则。
- 代码结构与设计原则
-
类与方法分布:
5个类,平均3.2个方法/类,方法长度合理(平均4.69语句/方法)。
问题:缺乏接口抽象,所有类为具体实现,难以扩展。
-
注释覆盖率:3.8%,注释严重不足。
问题:关键逻辑(如计费规则)无说明,维护困难。
- 输入与健壮性
-
输入验证缺失:
未测试重量是否为负数、日期格式是否合法。
直接使用Double.parseDouble()可能抛出NumberFormatException。
-
嵌套深度:
最大块深度3(如Order.calculateRate()中的多层if-else)。
题目集九
第二次代码虽然也未在规定时间内取得满分,但是相对前一轮迭代已经取得进步。
类设计

具体分析
- 类结构与职责划分
-
Customer及其子类:
职责:管理客户信息及折扣率。
通过抽象类Customer定义折扣率抽象方法getDiscountRate(),子类(IndividualCustomer、CorporateCustomer)实现具体折扣逻辑。
-
Goods及其子类:
职责:管理货物信息、计费重量及费率计算。
抽象类Goods封装计费重量计算,子类(NormalGoods、DangerousGoods等)实现具体费率逻辑。
-
PaymentMethod及其子类:
职责:定义支付方式的前缀描述。
通过抽象类实现多态支付方式,支持扩展(如新增CreditCardPayment)。
-
Order类:
职责:聚合订单相关信息(客户、航班、货物、支付方式),计算总运费。
通过组合(而非继承)关联其他类,符合合成复用原则。
- 面向对象设计原则应用
- 封装原则:
各业务类通过 private/protected 修饰符隐藏内部状态
数据访问通过getter实现 - 继承与多态:
通过抽象类 + 子类实现类型扩展(客户 / 货物 / 支付方式)
多态应用:customer.getDiscountRate() 根据客户类型返回不同折扣 - 单一职责原则:
Goods类专注货物属性与计费逻辑
PaymentMethod类专注支付方式的展示逻辑
Order类专注订单信息整合与费用计算 - 开闭原则:
系统可通过新增子类(如VIPCustomer/FragileGoods)扩展功能
SourceMonitor图

- 代码复杂度与结构
1.1 复杂度分析
- 最复杂方法:ExpediteGoods.calculateRate()(复杂度5,行号78)。
问题:包含多层if-else分支,直接硬编码费率规则,违反开闭原则。新增货物类型需修改方法,扩展性差。
- 平均复杂度:1.62,整体较低,但存在局部高复杂度方法。
- 类与设计原则
2.1 类职责分析
- Goods类:
职责:管理货物属性、计算计费重量、计算费率。
问题:违反单一职责原则,应拆分为:
Goods:仅管理属性(尺寸、重量)。
ChargeableWeightCalculator:计算计费重量。
RateStrategy:处理费率计算。
- Order类:
职责:聚合订单信息、计算总运费。
问题:运费计算逻辑直接耦合在Order中,应分离为FreightService类。
2.2 设计原则符合性
- 开闭原则:
符合点:用户类型(Customer子类)、支付方式(PaymentMethod子类)通过继承扩展。
违反点:货物类型的费率计算硬编码在Goods子类中,扩展需修改代码。
- 依赖倒转原则:
违反点:Order类直接依赖具体Goods子类,应依赖RateStrategy接口。
- 里氏替换原则:
符合点:IndividualCustomer和CorporateCustomer可替换父类Customer,行为一致。
3. 代码质量与健壮性
3.1 注释与可读性
- 注释覆盖率:0%(指标中“Percent Lines with Comments”为0.0%)。
问题:关键逻辑(如费率计算、折扣应用)无注释,维护困难。
3.2 输入验证
问题:
未校验输入数据(如重量负数、无效日期)。
直接使用Double.parseDouble()可能抛出未处理的NumberFormatException。
3.3 异常处理
问题:
在Main类中直接抛出IllegalArgumentException,未处理异常。
三、踩坑心得
第一次代码
- 输入格式与顺序的严格匹配问题
坑点:输入数据时未严格遵循题目要求的顺序,导致数据解析错位。例如,读取货物数量后未正确跳过换行符,或日期格式未按yyyy-MM-dd输入,导致LocalDate.parse抛出异常。
原因:Scanner读取不同类型数据(如nextLine()与nextInt())时可能残留换行符,或用户输入格式与代码预期不符。
解决方案:
输入字符串前使用scanner.nextLine(),明确提示用户输入格式(如日期格式),或在代码中添加格式校验。 - 计费重量与费率计算的边界条件处理
坑点:分段费率的边界值判断错误(如重量=20kg时误用35元/kg),或体积重量计算时未取整数(题目示例中体积重量为整数,但实际可能为小数)。
原因:代码中calculateRate方法的条件判断为if (chargeableWeight < 20),而边界值20kg应属于20≤重量<50区间,需注意else if的顺序。
解决方案:
用测试用例验证边界值:如19.9kg→35元、20kg→30元、50kg→25元、100kg→15元。
体积重量计算结果保留小数(如96.5kg),计费重量取最大值时需用Math.max正确比较。 - 输出格式与精度的细节误差
坑点:运费金额未按要求保留一位小数,或报表字段顺序与题目示例不符。
原因:printf格式字符串中的%.1f未正确应用,或货物明细的表头顺序错误。
解决方案:
验证输出样例:如 “微信支付金额:XX.X”“计费重量:XX.X”,确保小数位一致。
按题目要求排列报表字段:“明细编号 \t 货物名称 \t 计费重量 \t 计费费率 \t 应交运费”。
第二次代码
一、载重检查逻辑错误:
代码中使用order.getTotalActualWeight()检查航班载重,但根据要求,空运计费重量为实际重量与体积重量的较大值,而航班载重应基于计费重量进行检查。若仅用实际重量,可能导致航班超载(例如货物体积大但实际重量轻时,计费重量可能远超实际重量)。
二、折扣率应用场景误解
文档中说明 “折扣率针对每个订单的运费”,即总运费 =(各货物计费重量 × 费率)之和 × 折扣率,代码中Order.getTotalPayment()的计算逻辑正确。但需注意:若未来需求改为 “按货物类型分别应用折扣”,当前设计需调整。
解决方法:在Order类中新增getTotalChargeableWeight()方法,累加所有货物的计费重量,用于载重校验。
四、改进建议
- 第一次代码
- 遵循单一职责原则(SRP)
1.1 分离费率计算逻辑
问题:Order类同时管理货物列表和费率计算,职责不单一。
改进方案:
定义RateStrategy接口,通过策略模式将费率计算逻辑独立。
创建具体费率策略类。
2. 支持货物类型差异化费率
2.1 添加货物类型字段
问题:Cargo类未区分货物类型,无法应用不同费率。
改进方案:
在Cargo类中添加type字段,并在创建货物时指定类型。
- 第二次代码
- 遵循单一职责原则(SRP)
1.1 拆分Goods类的职责
问题:Goods类同时管理属性、计费重量和费率计算,职责过重。
改进方案:
将计费重量计算和费率计算分离为独立类。
使用策略模式封装费率计算逻辑。
2. 使用工厂模式优化对象创建
2.1 支付方式工厂
问题:Main类中的switch语句直接创建支付对象,违反开闭原则。
改进方案:
创建PaymentFactory类,封装支付对象的创建逻辑。
五、总结
在这轮迭代中,虽仍未取得过满分,但是相比前一轮都为0分来说已经取得了小小的进步(自认为),希望在下一次作业中能更进一步,以自己的能力写出满分代码。在作业期间,从一开始的束手无措到能自己着手写出大概的框架,这都是进步的表现。除去进步的表现,还有许多要去改正和学习的地方。例如设计原则的深度应用,当前Main方法中直接通过if-else创建具体子类,违背 “依赖抽象而非具体” 的原则。需学习工厂模式或抽象工厂模式,通过接口创建对象。总的来说,只要保持学习的心,不怠惰不放弃,就一定能有所收获!

浙公网安备 33010602011771号