题目集8-9总结性Blog:
一、前言
作为一名普通的计算机专业学生,面对题目集8和9的“航空货运管理系统”时,我真切感受到了从“知道概念”到“熟练应用”之间的巨大鸿沟。这两道题就像一面镜子,清晰映照出我在面向对象设计(OOD)理论与实践结合上的薄弱之处。即使用尽全力也无法战胜啊,最终以一道半对一道没分的成绩结束了作业,不过在学习与做题的过程中还是学习到了很多东西,接下来由我一一分享。
(一)知识点总结
题目集8(航空货运管理系统-类设计)
- 核心考查:单一职责原则、里氏代换原则、开闭原则、合成复用原则。
- 设计要点:
- 需设计
客户类、货物类、航班类、订单类。 - 实现逻辑:
- 计费重量计算(体积重量与实际重量取较大值)。
- 分段费率计算(根据重量区间确定费率)。
- 订单与航班载重量校验。
- 需设计
题目集9(航空货运管理系统-继承与多态)
- 新增设计:继承与多态设计,引入:
- 客户类型(
个人/集团) - 货物类型(
普通/加急/危险) - 支付方式(枚举类型)
- 客户类型(
- 扩展考查:依赖倒转原则,需通过抽象类或接口实现:
- 客户折扣的多态计算(个人用户9折/集团用户8折)。
- 货物费率的多态计算(不同类型货物对应不同费率)。
- 支持后续新增类型的扩展。
(二)题量与难度
题量
- 两次题目集均以“航空货运管理系统”为核心题目,辅以其他基础编程题,总题量约3题/次。
难度
| 题目集 | 难度级别 | 核心挑战 |
|---|---|---|
| 题目集8 | 中等 | 类职责划分(如货物类负责计费重量计算,订单类负责费用汇总),避免类职责混杂。 |
| 题目集9 | 较难 | 设计继承体系(如客户基类派生出个人客户和集团客户),通过多态实现费率计算和折扣逻辑,处理支付方式枚举设计。 |
二、设计与分析:在试错中理解设计原则
(一)题目集8:从“功能实现”到“原则遵守”的挣扎
1. 类设计的“试错三部曲”
-
第一版:职责混乱的“大杂烩”
初期我将所有逻辑都塞进Main类,客户信息、货物计算、航班校验全在main方法里完成。这导致代码长达400行,修改一个小功能就要在几百行代码里“找来找去”,完全违背单一职责原则。
反思:就像把所有工具扔进一个抽屉,想用的时候根本找不到——类必须有清晰的职责边界。 -
第二版:机械拆分的“表面功夫”
我仿照教材案例,简单拆分出Customer、Cargo、Flight、Order四个类,但每个类的方法设计依然混乱。例如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个,通过抽象类(
Customer、Cargo)和接口(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() { ... }
四、改进建议:
(一)代码层面:让变更更简单
- 策略模式重构费率计算
- 创建
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子类通过组合策略对象实现费率计算,而非硬编码。
- 创建
(二)类设计层面:让职责更纯粹
-
拆分
Order类职责- 根据SourceMonitor的类复杂度分析,
Order类承担信息存储与费用计算双重职责(复杂度=5),可拆分为:OrderInfo类:存储订单基础信息(发件人、收件人等)。OrderCalculator类:专注费用计算(依赖Customer和Cargo策略)。
- 根据SourceMonitor的类复杂度分析,
-
工厂模式创建对象
- 使用
CustomerFactory和CargoFactory解耦对象创建逻辑,避免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("非法客户类型"); }; } }
- 使用
(三)测试层面:让错误提前暴露
-
单元测试覆盖多态逻辑
- 针对
Customer和Cargo子类编写测试,验证折扣和费率计算:@Test public void testCorporateCustomerDiscount() { Customer corporate = CustomerFactory.createCustomer("Corporate"); assertEquals(0.8, corporate.getDiscountRate(), 0.01); }
- 针对
-
边界值与异常测试
- 测试题目集8的临界重量(如2kg、20kg)和题目集9的非法输入(如错误的货物类型),利用SourceMonitor的代码覆盖率报告确保测试全面性。
五、总结:
(一)从“完成任务”到“理解设计”的蜕变
通过SourceMonitor的代码度量数据,我直观看到了设计原则对代码质量的影响:
- 题目集8因硬编码和职责混杂,导致代码可维护性评分低(注释少、复杂度集中)。
- 题目集9通过多态和接口设计,降低了类耦合度,但注释和测试的缺失仍需改进。
(二)不完美的代码,却真诚的成长
我的代码依然存在不足,但SourceMonitor的分析报告为改进提供了明确方向:
- 短期:补充注释,确保类图与代码一致;
- 长期:深入理解策略模式、工厂模式,避免过度依赖继承;
- 习惯:编码前绘制类图,编码后用工具(如SourceMonitor)自动检测设计原则遵守情况。
(四)写给未来的自己
面向对象设计的学习之路还很长,我知道自己可能还会在继承 组合 抽象类 接口的学习上停留很久,还会在多态调试中抓耳挠腮。但现在的我,已经逐渐有了面对挑战的勇气——因为我明白:每一次为设计原则而“纠结”的时侯,都是向合格开发者迈进的脚印。未来的日子里,我会带着这份“不完美”的经验,继续在代码的世界里摸索前行。或许终有一天,我能写出让自己满意的、符合设计原则的代码,但此刻,我更想感谢那个在深夜里反复修改类图、为了一个费率计算逻辑较真的自己——因为热爱,所以值得。


浙公网安备 33010602011771号