第二次Blog作业
目录
- 一.前言
- 二.设计与分析
- 三.踩坑心得
- 四.改进建议
- 五.总结
一.前言
总结第8,9次题目集的知识点、题量、难度等情况
8-1
- 知识点
- Element类及display()抽象方法的定义
- 承与多态的实现
- 类之间的关联关系
- 题量
- 设计4个类(Element、Point、Line、Plane),Main类处理输入输出
- 难度
- 简单,需要注意String.format
8-2
- 知识点
- 策略接口的应用,因为不同雨刷系统的行为差异
- 题量
- 设计多个类(如WiperSystem、LeverState、DialState等),逻辑复杂
- 难度
- 较高,需处理多种状态和处理办法,易遗漏导致错误
9-1
- 知识点
- RubikCube作为父类,SquareCube和RegularPyramidCube子类
- 几何计算公式
- 题量
- 需设计3-4个类,通过重写方法,利用数学公式实现
- 难度
- 中等,难点在于正确写出几何公式
9-2
- 知识点
- 容器类的使用
- 动态增删操作
- 题量
- 新增容器类GeometryObject,改造主类输入逻辑
- 难度
- 中等,需要注意检查输入数据合法性
二.设计与分析
<一>题集8-“航空货运管理系统”
以下是利用PowerDesigner制作的相应类图:

作为这个航空货运系统的要求,需要把单一职责、里氏代换、开闭、合成复用这四个设计原则揉进类结构里,让系统既好维护又能灵活扩展。
- 单一职责
- 支付模块:Payment是抽象父类,只定义pay()这个支付行为
WechatPay和AliPay作为子类,各自实现具体支付逻辑(比如微信走支付接口、支付宝走另一套)。它们只管“怎么付” - 航班模块:Flight类只封装航班号、起降城市、日期、最大载重这些基础信息,只负责“航班是谁、能运多少”,不和订单掺活、不搞计算逻辑
- 货物模块:Cargo类存储货物自身属性(长宽高、重量、ID)
通过strategy关联Fee(计费策略),把“怎么算运费”的逻辑给Fee类,自己只管货物 - 订单模块:Order类聚合航班、发收件信息、支付方式、货物列表
只负责订单的信息 - Report类 :专门生成订单报表和货物明细,只管输出报表
- 支付模块:Payment是抽象父类,只定义pay()这个支付行为
- 里氏代换
- 子类能在不破坏逻辑的前提下,替换父类的位置
- 支付部分:WechatPay和AliPay都继承Payment抽象类,并重写pay()方法
- 当订单里需要“支付”时,不管传的是WechatPay还是AliPay,调用payment.pay(amount)都能正常完成支付——子类的行为不会搞乱父类的抽象
- 开闭原则
- 支付方式扩展:如果以后要加“现金支付”
只需新建CashPay类继承Payment,重写pay()方法,Order类里用的是Payment抽象类引用,完全不用改,对扩展开放,对修改关闭 - 运费策略扩展
CalculateFee是计算运费的接口,BasicRateFee是基础费率实现,如果要加“易碎品费率”“加急费率”,只需新增FastFee等子类实现CalculateFee
- 支付方式扩展:如果以后要加“现金支付”
- 合成复用
- Order类里,航班信息是Flight对象、发收件是Message对象、支付方式是Payment对象、货物是List
——这些都是有一个的组合关系,而不是是一个(is-a)的继承。订单需要航班信息时,直接调flight.getFrom(),不需要继承航班的所有属性,解耦很彻底。 - Cargo类里的strategy属性(类型是Fee),也是组合:
货物的运费计算交给Fee的实现类(比如BasicRateFee),以后换费率策略,换一个strategy对象就行,货物本身不用动
- Order类里,航班信息是Flight对象、发收件是Message对象、支付方式是Payment对象、货物是List
还有一个非常重要的核心需求:货运计费
- 需要实际重量 vs 体积重量取高,再乘分段费率
- Cargo里先算体积重量,和实际重量比出计费重量
- 计费重量交给CalculateFee的实现类(如BasicRateFee),按分段公式算基础运费
- 各司其职单一职责,以后改费率公式只动BasicRateFee,货物逻辑不受影响开闭原则
接下来来分析SourceMontor的生成报表内容

- Statements(可执行语句数)
- 有效代码占比约51.5%
- Calls(方法调用数)
- 调用次数极少,方法内聚性过高
- Methods/Class(每类平均方法数)
- 每类≈7个方法,职责分配较均匀
- Avg Stmts/Method(每方法平均语句数)
- 方法过度碎片化(大量getter/setter或单语句方法)
- Max Complexity(最大圈复杂度)
- 最大仅2,所有方法逻辑极简单
- Avg Depth(平均嵌套深度)
- 整体逻辑较扁平,易读性基础
- Avg Complexity(平均圈复杂度)
- 大部分方法是“线性执行”(无分支或仅简单分支)

以上是类方法度量报表
- Cargo 类
- Getter/Setter 泛滥:如 getLength()、setHeight() 等方法
- 业务方法逻辑过简:calculateFee()、getFeeWeight()、getVolumeWeight() 等核心方法,Statements=1、Complexity=1
看似逻辑简单,实则可能把复杂逻辑给其他类
- Order 类
- Getter/Setter 重复问题
- 方法嵌套深:calculateFee()、calculateWeight() 等方法,Max Depth=3、Complexity=2
下面是雷达图和块直方图块直方图

- Methods/Class(每类平均方法数)
- 类的职责分配相对均匀
- Avg Stmts/Method(每方法平均语句数)
- 方法过度碎片化(如大量 getter/setter 或单语句方法),导致方法调用链冗长
- Max Complexity(最大圈复杂度)
- 单个方法的逻辑复杂度极低
- 存在少数嵌套较深的方法,需警惕逻辑可读性下降
块直方图
- 深度0 - 2:柱形极高 → 绝大多数代码的控制结构嵌套极浅(无嵌套或单层嵌套),易读性基础合格
- 深度3 - 4:柱形骤降 → 仅少数方法存在 3 - 4 层嵌套
- 深度5+:几乎无柱形 → 无极端嵌套的方法,整体逻辑“扁平化”
问题:
Cargo和Order类中90%方法是 Getter/Setter(如getHeight()、setId()),导致方法过度“碎片化”:单个方法仅1行代码,开发与维护效率极低
<二>题集8-“航空货运管理系统”
以下是利用PowerDesigner制作的相应类图:

第二次航空货运管理系统多了货物类型费率、用户折扣、现金支付这些新需求,这次重点在解耦计算规则、扩展支付/费率/折扣问题的东西
- 支付模块
利用“开闭原则”扩展支付方式- 第一次系统仅支持微信、支付宝支付,本次新增现金支付(Cash类)。类图中Payment作为抽象父类定义支付行为,WechatPay/AliPay/Cash作为子类重写 pay() 方法
- 新增支付方式时,仅需继承 Payment 编写新子类,无需修改原有支付逻辑
- 当客户选择现金支付时,只需将订单的 paymentMethod 替换为 Cash 对象,调用 pay(amount) 即可完成支付
- 费率模块
- 新需求中货物分普通、危险、加急三类
- 类图里 BasicRateFee 作为抽象父类定义费率计算逻辑,NormalFee(普通)、DangerousFee(危险)、ExpediteFee(加急)作为子类重写 getBaseFee(weight)
- 用策略模式解耦后,新增货物类型仅需扩展 BasicRateFee 子类,Cargo 和 Order 无需改动
- 折扣模块
- 类图中 Discount 作为抽象父类定义折扣逻辑,IndividualDiscount(个人)、CorporateDiscount(集团)作为子类重写 getDiscount(amount)
- Order 类通过聚合 Discount 对象实现
- 计算最终运费时,先累加所有货物基础运费,再调用 discountStrategy 应用打几折
- 多货物
- 以 List
cargoList 存储货物 - 添加货物时调用 order.addCargo(cargo)
- 货物数量变化不影响订单逻辑,符合开闭原则
- 以 List
- 报表生成
- 调整报表仅需修改 OrderReport/CargoDetail,不影响逻辑(单一职责)
以下是第二次航空管理系统SourceMontor的生成报表内容:

- Files(文件数)(1)
- 所有类集中在单个文件,类与类的逻辑边界模糊
- Lines(总行数)(554)
- 代码规模属中小型范畴,但单文件18个类
- Statements(可执行语句数)(299)
- 有效代码占比约54%(299/554),但单文件下逻辑嵌套混杂
- % Branches(分支覆盖率)(13.7)
- 单元测试仅覆盖13.7%的代码分支
- Calls(方法调用数)(20)
- 方法间总调用次数极少,模块间协作性弱。原因是方法过度碎片化(大量getter/setter
- Methods/Class(每类平均方法数)(4.56)
- 每类约5个方法,职责分配看似均匀
- Avg Stmts/Method(每方法平均语句数)(1.63)
- 方法过度碎片化,大量方法为getter/setter或单语句逻辑(如cargo.setType("危险"))
- Max Complexity(最大圈复杂度)(7)
- 存在圈复杂度为7的方法,原因是订单总运费计算逻辑(需遍历多货物、判断货物类型费率、叠加用户折扣)
- Avg Depth(平均嵌套深度)(1.69)
- 整体逻辑平均嵌套1.69层,看似较扁平,但是又有圈复杂度7的复杂方法,核心方法嵌套深
- Avg Complexity(平均圈复杂度)(1.33)
- 多数方法为“线性执行”(无分支或仅简单分支)


-
Cargo 类
- Getter/Setter 泛滥:如 getHeight()、setHeight()、getId() 等方法,调用冗长且维护时易漏
- Cargo() 构造方法 Metrics 为 (1, 8, 2, 1) → 含8条可执行语句、嵌套深度2
大概率因为初始化阶段批量设置长宽高、重量等属性 - createFee() 方法 Metrics 为 (4, 7, 4, 1) → 圈复杂度4、嵌套深度4
该方法承担“根据货物类型(普通/危险/加急)分支创建费率策略”的复杂逻辑,违背开闭原则
-
Order 类
- 计算方法“嵌套过深”
calculateFee()、calculateWeight() 方法 Metrics 为 (2, 4, 3, 1)、(2, 4, 3, 0)
**货物有效性校验、用户折扣分层计算 ** 导致逻辑嵌套 - getDiscount() 方法 Metrics 为 (4, 7, 4, 0)
新增“用户6折”等规则时,需直接修改方法内部,违背开闭原则**
- 计算方法“嵌套过深”

块直方图:
- 深度 0 - 2:大多数代码的控制结构嵌套极浅
- 逻辑被过度拆分(核心功能分散在大量简单方法中)
- 深度 3 - 4:方法大概率是订单总运费计算、危险货物费率
- 深度 5+:无极端嵌套的方法
三.踩坑心得
<一>方法碎片化
Avg Stmts/Method 靠近中心,表明方法过度碎片化,有大量 Getter/Setter 和单语句方法。
<二>Order.calculateFee方法嵌套深
点击查看代码
public double calculateFee(){
double total = 0;
for (Cargo cargo:cargos) {
total+=cargo.calculateFee();
}
return discount.getDiscount(total);
}
点击查看代码
interface Fee{
double calculateFee(Cargo cargo);
}
abstract class BasicFee implements Fee{
protected abstract double getBaseFee(double weight);
@Override
public double calculateFee(Cargo cargo) {
double w=cargo.getFeeWeight();
double t=w*getBaseFee(w);
return t;
}
}
四.改进建议
- 对 Order.calculateFee() 等嵌套深的方法,拆分为 calculateCargoBaseFees()(累加基础运费)、applyUserDiscount()(叠加折扣)等子方法,降低嵌套深度
- 按职责划分包结构(如 payment 包放支付类、fee 包放费率类),实现“修改现金支付逻辑仅动 payment 包,不影响 fee 包费率计算”的解耦
- 比如对“危险货物费率修正”等局部逻辑,提取为私有方法,降低主方法嵌套
点击查看代码
public class Order {
private double calculateDangerousCargoFee(Cargo cargo) {
return cargo.getType().equals("危险") ? cargo.calculateBaseFee() * 2 : cargo.calculateBaseFee();
}
五.总结
- 迭代开发中,我掌握了单一职责、开闭原则等设计准则
- 将支付方式(WechatPay/AliPay/Cash)、费率策略(NormalFee/DangerousFee)、用户折扣(IndividualDiscount/CorporateDiscount)解耦为了可扩展子类
- 分析 SourceMonitor 报表,我学会从方法粒度、圈复杂度、嵌套深度等诊断代码的质量
- 虽然不是很熟练,但是我开始尝试了模式的应用
- 策略模式(如用DiscountStrategy接口管理折扣)
- 工厂模式(OrderFactory封装订单)
通过航空货运系统开发与 SourceMonitor 报表分析,我知道了:写“能跑的代码”只是起点,“易维护、可扩展”的代码,需要兼顾设计原则、多种模式共同发力。
浙公网安备 33010602011771号