0625s

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

题目集8-9总结性Blog:

一、前言

作为一名普通的计算机专业学生,面对题目集8和9的“航空货运管理系统”时,我真切感受到了从“知道概念”到“熟练应用”之间的巨大鸿沟。这两道题就像一面镜子,清晰映照出我在面向对象设计(OOD)理论与实践结合上的薄弱之处。即使用尽全力也无法战胜啊,最终以一道半对一道没分的成绩结束了作业,不过在学习与做题的过程中还是学习到了很多东西,接下来由我一一分享。

(一)知识点总结

题目集8(航空货运管理系统-类设计)

  • 核心考查:单一职责原则、里氏代换原则、开闭原则、合成复用原则。
  • 设计要点
    • 需设计客户类货物类航班类订单类
    • 实现逻辑:
      • 计费重量计算(体积重量与实际重量取较大值)。
      • 分段费率计算(根据重量区间确定费率)。
      • 订单与航班载重量校验。

题目集9(航空货运管理系统-继承与多态)

  • 新增设计:继承与多态设计,引入:
    • 客户类型(个人/集团
    • 货物类型(普通/加急/危险
    • 支付方式(枚举类型)
  • 扩展考查:依赖倒转原则,需通过抽象类或接口实现:
    • 客户折扣的多态计算(个人用户9折/集团用户8折)。
    • 货物费率的多态计算(不同类型货物对应不同费率)。
    • 支持后续新增类型的扩展。

(二)题量与难度

题量

  • 两次题目集均以“航空货运管理系统”为核心题目,辅以其他基础编程题,总题量约3题/次

难度

题目集 难度级别 核心挑战
题目集8 中等 类职责划分(如货物类负责计费重量计算,订单类负责费用汇总),避免类职责混杂。
题目集9 较难 设计继承体系(如客户基类派生出个人客户集团客户),通过多态实现费率计算和折扣逻辑,处理支付方式枚举设计。

二、设计与分析:在试错中理解设计原则

(一)题目集8:从“功能实现”到“原则遵守”的挣扎

1. 类设计的“试错三部曲”

  • 第一版:职责混乱的“大杂烩”
    初期我将所有逻辑都塞进Main类,客户信息货物计算航班校验全在main方法里完成。这导致代码长达400行,修改一个小功能就要在几百行代码里“找来找去”,完全违背单一职责原则。
    反思:就像把所有工具扔进一个抽屉,想用的时候根本找不到——类必须有清晰的职责边界。

  • 第二版:机械拆分的“表面功夫”
    我仿照教材案例,简单拆分出CustomerCargoFlightOrder四个类,但每个类的方法设计依然混乱。例如Flight类既负责存储航班信息,又包含calculateTotalFee()这样的费用计算方法,职责重叠。
    教训:不是把代码分到不同类里就叫“面向对象”,类的方法必须围绕其核心职责。

  • 第三版:基于原则的“职责重构”

    • Cargo类只负责计算计费重量(体积重量与实际重量取较大值),新增calculateChargeWeight()方法;
    • Flight类专注管理载重量,通过addCargoWeight()方法校验是否超载,新增currentWeight属性记录已用载重;
    • Order类负责汇总订单信息和费用计算,但费率分段逻辑仍硬编码在其中(这成为后来的扣分点)。

    题目集8航空货运管理系统类图

    心得:当我能说出“这个类为什么要有这个方法”时,才算是真正理解了单一职责原则。

2. 硬编码的代价:违背开闭原则的“定时炸弹”

题目集8中,费率计算逻辑硬编码在Order类中:

if (chargeWeight < 2) {  
    rate = 3;  
} else if (chargeWeight < 20) {  
    rate = 25;  
} else if (chargeWeight < 200) {  
    rate = 20;  
} else {  
    rate = 15;  
}  

问题分析

  • SourceMonitor显示Order类的calculateTotalFee方法复杂度为6,虽不算极高,但硬编码导致逻辑与类强耦合。
    题目集8代码度量分析(SourceMonitor图)

  • 若新增费率分段,必须修改Order类,违背开闭原则。

  • 改进方向:将费率逻辑封装为独立策略类(如RateStrategy),通过接口注入Order类。

(二)题目集9:多态设计的“从无到有”

1. 继承体系的搭建:像搭积木一样设计类

题目集9航空货运管理系统类图

题目集9代码度量分析(SourceMonitor图)

  • 关键指标
    • 代码行数(Lines):380行,较题目集8减少,多态设计简化了条件判断逻辑。
    • 注释比例(% Comments):2.1%,依然偏低,多态逻辑缺乏必要说明。
    • 类数量(Classes):11个,通过抽象类(CustomerCargo)和接口(PaymentStrategy)实现多态,符合依赖倒转原则。
  • 雷达图分析
    分支语句比例(3.6%)显著下降,多态减少了if-else嵌套,但注释缺失和支付方式枚举处理仍需优化。

2. 依赖倒转原则的实践:面向接口编程

为处理支付方式,定义PaymentStrategy接口:

interface PaymentStrategy {  
    String getPaymentName();  
}  
enum WechatPayment implements PaymentStrategy {  
    INSTANCE;  
    @Override  
    public String getPaymentName() {  
        return "微信";  
    }  
}  

优势

  • SourceMonitor显示Order类对PaymentStrategy的依赖度低,符合“依赖抽象”原则。
  • 新增支付方式(如CashPayment)时,只需实现接口,无需修改Order类,满足开闭原则。

3. 多态带来的“甜蜜烦恼”:调试复杂度上升

多态逻辑导致调试时需追踪子类实现。例如,ExpediteCargo未重写getRate()时,默认调用Cargo父类方法,导致费率错误。
SourceMonitor提示

  • 子类方法覆盖不全可能导致隐性bug,需通过单元测试强制验证抽象方法实现(如使用JUnit的@Override注解校验)。

三、采坑心得:那些让人“拍脑门”的错误

(一)载重量校验:“计费重量” vs “实际重量”的血案

题目集8初期错误使用实际重量校验载重,SourceMonitor显示Order类的calculateTotalWeight方法调用Cargo.getWeight()而非calculateChargeWeight(),导致逻辑错误。
解决

// 错误代码  
double totalWeight = cargos.stream().mapToDouble(Cargo::getWeight).sum();  

// 修正后  
double totalWeight = cargos.stream().mapToDouble(Cargo::calculateChargeWeight).sum();  

(二)枚举输入:大小写敏感引发的“崩溃”

题目集9中,未处理输入大小写导致IllegalArgumentException。SourceMonitor显示CargoType枚举的使用位置缺少输入转换逻辑,需增加:

String inputType = scanner.next().toUpperCase();  

(三)类图与代码“两张皮”:设计与实现的脱节

SourceMonitor的类结构报告显示,代码中Order类的方法名(computeTotalCharge)与类图(calculateFee)不一致,暴露了设计与编码的脱节。
反思

  • 应使用UML工具(如StarUML)同步更新类图,或在编码前通过注释明确类接口:
    /**  
     * 计算订单总费用(对应类图中的calculateFee方法)  
     */  
    public double computeTotalCharge() { ... }  
    

四、改进建议:

(一)代码层面:让变更更简单

  1. 策略模式重构费率计算
    • 创建RateStrategy接口及实现类,将题目集8的分段逻辑和题目集9的货物类型费率统一管理:
      interface RateStrategy {  
          double getRate(double chargeWeight);  
      }  
      class NormalCargoStrategy implements RateStrategy {  
          @Override  
          public double getRate(double chargeWeight) {  
              return chargeWeight < 2 ? 3 : chargeWeight < 20 ? 25 : ...; // 分段逻辑  
          }  
      }  
      
    • Cargo子类通过组合策略对象实现费率计算,而非硬编码。

(二)类设计层面:让职责更纯粹

  1. 拆分Order类职责

    • 根据SourceMonitor的类复杂度分析,Order类承担信息存储与费用计算双重职责(复杂度=5),可拆分为:
      • OrderInfo类:存储订单基础信息(发件人、收件人等)。
      • OrderCalculator类:专注费用计算(依赖CustomerCargo策略)。
  2. 工厂模式创建对象

    • 使用CustomerFactoryCargoFactory解耦对象创建逻辑,避免Order类直接依赖具体子类:
      public class CustomerFactory {  
          public static Customer createCustomer(String type) {  
              return switch (type.toUpperCase()) {  
                  case "INDIVIDUAL" -> new IndividualCustomer();  
                  case "CORPORATE" -> new CorporateCustomer();  
                  default -> throw new IllegalArgumentException("非法客户类型");  
              };  
          }  
      }  
      

(三)测试层面:让错误提前暴露

  1. 单元测试覆盖多态逻辑

    • 针对CustomerCargo子类编写测试,验证折扣和费率计算:
      @Test  
      public void testCorporateCustomerDiscount() {  
          Customer corporate = CustomerFactory.createCustomer("Corporate");  
          assertEquals(0.8, corporate.getDiscountRate(), 0.01);  
      }  
      
  2. 边界值与异常测试

    • 测试题目集8的临界重量(如2kg、20kg)和题目集9的非法输入(如错误的货物类型),利用SourceMonitor的代码覆盖率报告确保测试全面性。

五、总结:

(一)从“完成任务”到“理解设计”的蜕变

通过SourceMonitor的代码度量数据,我直观看到了设计原则对代码质量的影响:

  • 题目集8因硬编码和职责混杂,导致代码可维护性评分低(注释少、复杂度集中)。
  • 题目集9通过多态和接口设计,降低了类耦合度,但注释和测试的缺失仍需改进。

(二)不完美的代码,却真诚的成长

我的代码依然存在不足,但SourceMonitor的分析报告为改进提供了明确方向:

  • 短期:补充注释,确保类图与代码一致;
  • 长期:深入理解策略模式、工厂模式,避免过度依赖继承;
  • 习惯:编码前绘制类图,编码后用工具(如SourceMonitor)自动检测设计原则遵守情况。

(四)写给未来的自己

面向对象设计的学习之路还很长,我知道自己可能还会在继承 组合 抽象类 接口的学习上停留很久,还会在多态调试中抓耳挠腮。但现在的我,已经逐渐有了面对挑战的勇气——因为我明白:每一次为设计原则而“纠结”的时侯,都是向合格开发者迈进的脚印。未来的日子里,我会带着这份“不完美”的经验,继续在代码的世界里摸索前行。或许终有一天,我能写出让自己满意的、符合设计原则的代码,但此刻,我更想感谢那个在深夜里反复修改类图、为了一个费率计算逻辑较真的自己——因为热爱,所以值得。

posted on 2025-05-25 12:27  Stt0625  阅读(22)  评论(0)    收藏  举报