第二次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类 :专门生成订单报表和货物明细,只管输出报表
  • 里氏代换
    • 子类能在不破坏逻辑的前提下,替换父类的位置
    • 支付部分: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对象就行,货物本身不用动

还有一个非常重要的核心需求:货运计费

  • 需要实际重量 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)
    • 货物数量变化不影响订单逻辑,符合开闭原则
  • 报表生成
    • 调整报表仅需修改 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);
    }
Fee接口里面的calculateFee和子类继承
点击查看代码
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;
    }
}
这样子确实嵌套太深了

四.改进建议

  1. 对 Order.calculateFee() 等嵌套深的方法,拆分为 calculateCargoBaseFees()(累加基础运费)、applyUserDiscount()(叠加折扣)等子方法,降低嵌套深度
  2. 按职责划分包结构(如 payment 包放支付类、fee 包放费率类),实现“修改现金支付逻辑仅动 payment 包,不影响 fee 包费率计算”的解耦
  3. 比如对“危险货物费率修正”等局部逻辑,提取为私有方法,降低主方法嵌套
点击查看代码
public class Order {  
    private double calculateDangerousCargoFee(Cargo cargo) {  
        return cargo.getType().equals("危险") ? cargo.calculateBaseFee() * 2 : cargo.calculateBaseFee();  
    }  

五.总结

  1. 迭代开发中,我掌握了单一职责、开闭原则等设计准则
  • 将支付方式(WechatPay/AliPay/Cash)、费率策略(NormalFee/DangerousFee)、用户折扣(IndividualDiscount/CorporateDiscount)解耦为了可扩展子类
  1. 分析 SourceMonitor 报表,我学会从方法粒度、圈复杂度、嵌套深度等诊断代码的质量
  2. 虽然不是很熟练,但是我开始尝试了模式的应用
  • 策略模式(如用DiscountStrategy接口管理折扣)
  • 工厂模式(OrderFactory封装订单)
    通过航空货运系统开发与 SourceMonitor 报表分析,我知道了:写“能跑的代码”只是起点,“易维护、可扩展”的代码,需要兼顾设计原则、多种模式共同发力。
posted @ 2025-05-24 19:07  24201839-薛文韬  阅读(17)  评论(0)    收藏  举报