题目集8~9航空货运管理系统的总结性Blog

前言

普通的类的设计需要耐心打磨但简单但繁琐,在前几次的题目中,我已将类的封装练习的较为熟练。但面向对象的真正的魅力,我想并不是局限在基础的类与对象的设计,同样重要的是封装与继承、多态协同的威力——这正是我在近期航空货运管理系统编程实践中的深刻感悟。在这两次编程练习中,我将很多知识点的理论运用到了实践中。这次练习虽不复杂,但很重要的我的细节的思考与设计中,体会到的继承与多态的魅力所在。在两次系统设计中,我尝试将理论知识转化为可运行的代码架构。以货物管理模块为例,我定义抽象基类封装货物的基础属性与运输接口,再通过普通货物、加急货物等子类继承基类,并重写计费规则、优先级处理等方法。当不同类型的货物对象通过统一接口触发差异化行为时,多态机制让代码展现出 “同一消息,不同实现” 的灵动性,而继承体系则像一棵逻辑清晰的树,让共性与特性在层次结构中自然生长。

设计与分析

第一次航空货运管理系统题目

航空货运系统的核心功能包括:

客户管理:记录客户基本信息(姓名、电话、地址)及唯一标识;
货物计费:根据货物重量或体积(抛重)计算计费重量,并按阶梯费率计算运费;
航班管理:校验订单总重量是否超过航班载重限制;
订单处理:整合客户、货物、航班信息,生成订单并输出详情。

继承与抽象:

Human父类:封装 “人” 的通用属性(姓名、电话、地址),作为客户、发件人、收件人的基础类。

点击查看代码
class Human {
    private String name, phoneNumber, address;
    // 构造方法、getter/setter
}

Customer子类:继承Human,扩展客户特有的id属性,体现 “客户是特殊类型的人” 的逻辑。

点击查看代码
class Customer extends Human {
    private String id;
    public Customer(String id, String name, String phone, String addr) {
        super(name, phone, addr); // 复用父类构造逻辑
        this.id = id;
    }
}

下面附上我的类设计图

接下来是我的程序在SourceMonitor中的分析结果

点击查看详细分析结果
Metrics Details For File 'Main.java'
--------------------------------------------------------------------------------------------
Parameter				Value
=========				=====
Project Directory			D:\JAVA文件\FlightOrder1\src\
Project Name				
Checkpoint Name				Baseline
File Name				Main.java
Lines					407
Statements				211
Percent Branch Statements		2.8
Method Call Statements			9
Percent Lines with Comments		0.0
Classes and Interfaces			8
Methods per Class			16.50
Average Statements per Method		0.78
Line Number of Most Complex Method	55
Name of Most Complex Method		ActualRateStrategy.setId()
Maximum Complexity			5
Line Number of Deepest Block		66
Maximum Block Depth			3
Average Block Depth			1.46
Average Complexity			1.09
--------------------------------------------------------------------------------------------
Most Complex Methods in 7 Class(es):	Complexity, Statements, Max Depth, Calls
ActualRateStrategy.setId()		5, 8, 3, 0
Customer.Customer()			1, 2, 2, 1
Customer.Customer()			1, 0, 0, 0
Customer.getId()			1, 1, 2, 0
Customer.setId()			1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 5, 2, 0
Flight.setId()				1, 0, 0, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 2, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 9, 2, 0
Goods.setId()				1, 0, 0, 0
Human.getAddress()			1, 1, 2, 0
Human.getName()				1, 1, 2, 0
Human.getPhoneNumber()			1, 1, 2, 0
Human.Human()				1, 3, 2, 0
Human.Human()				1, 0, 0, 0
Human.setAddress()			1, 1, 2, 0
Human.setName()				1, 1, 2, 0
Human.setPhoneNumber()			1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				2, 4, 3, 1
Order.setId()				2, 3, 2, 1
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 8, 2, 0
Order.setId()				1, 0, 0, 0
OrderPrinter.setId()			1, 1, 2, 0


在我的程序中,平均方法复杂度1.09,96%的方法复杂度为1,经过检查我发现大量方法为属性操作,缺乏复杂业务逻辑。
而最大复杂度却超过了合理的水平,ActualRateStrategy.setId()复杂度5、8条语句、3层嵌套,是该系统最复杂的方法。
综上可知,我的系统虽然是实现了全部的功能,但是设计还是不尽合理。

第二次航空货运管理系统题目

在第一次题目的基础上,我引入了枚举类型、多策略计费规则、客户类型折扣及支付方式适配。
CustomerType枚举:定义客户类型为Individual(个人)和Corporate(企业),替代字符串硬编码,提升代码可读性与安全性。

点击查看代码
enum CustomerType { Individual, Corporate }

在Order类计算总费用时,根据客户类型应用折扣:

点击查看代码
public void setSumPay() {
    double sum = goodsArrayList.stream().mapToDouble(Goods::getFee).sum();
    if (customer.getCustomerType() == CustomerType.Individual) {
        sum *= 0.9; // 个人客户9折
    } else {
        sum *= 0.8; // 企业客户8折
    }
    this.sumPay = sum;
}

新增支付类型:通过payType字段支持 “微信支付”“支付宝支付”“现金支付”,在打印订单时动态显示支付方式。

点击查看代码
if (order.getPayType().equals("Wechat")) {
    System.out.print("微信支付");
} else if (order.getPayType().equals("ALiPay")) {
    System.out.print("支付宝支付");
} else {
    System.out.print("现金支付");
}

新增策略实现类:
ExpediteRateStrategy(加急件费率,比普通件高 50%);
DangerousRateStrategy(危险品费率,含安全附加费)。

点击查看代码
class ExpediteRateStrategy implements RateStrategy {
    public double calculateRate(double weight) {
        // 加急件费率为普通件的1.5倍(示例逻辑)
    }
}

另外,我还将第一次题目中的Human类更名为OrderObject,移除 “人” 的强假设,允许发件人 / 收件人是企业或其他实体

下面附上我自己程序的类图

接下来是我的程序在SourceMonitor中的分析结果

点击查看详细分析结果

Metrics Details For File 'Main.java'
--------------------------------------------------------------------------------------------

Parameter				Value
=========				=====
Project Directory			D:\JAVA文件\FlightOrder\src\
Project Name				
Checkpoint Name				Baseline
File Name				Main.java
Lines					455
Statements				325
Percent Branch Statements		8.6
Method Call Statements			18
Percent Lines with Comments		0.0
Classes and Interfaces			12
Methods per Class			14.33
Average Statements per Method		1.16
Line Number of Most Complex Method	44
Name of Most Complex Method		DangerousRateStrategy.setId()
Maximum Complexity			5
Line Number of Deepest Block		123
Maximum Block Depth			4
Average Block Depth			1.71
Average Complexity			1.33

--------------------------------------------------------------------------------------------
Most Complex Methods in 10 Class(es):	Complexity, Statements, Max Depth, Calls

Customer.Customer()			1, 3, 2, 1
Customer.Customer()			1, 0, 0, 0
Customer.getCustomerType()		1, 1, 2, 0
Customer.getId()			1, 1, 2, 0
Customer.setCustomerType()		1, 1, 2, 0
Customer.setId()			1, 1, 2, 0
DangerousRateStrategy.setId()		5, 8, 3, 0
ExpediteRateStrategy.setId()		5, 8, 3, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 1, 2, 0
Flight.setId()				1, 5, 2, 0
Flight.setId()				1, 0, 0, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 2, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				1, 1, 2, 0
Goods.setId()				5, 10, 4, 0
Goods.setId()				1, 10, 2, 0
Goods.setId()				1, 0, 0, 0
Main.setId()				3, 48, 3, 2
NormalRateStrategy.setId()		5, 8, 3, 0
Order.setId()				1, 1, 2, 0
Order.setId()				4, 8, 3, 3
Order.setId()				2, 3, 2, 1
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 1, 2, 0
Order.setId()				1, 9, 2, 0
Order.setId()				1, 0, 0, 0
OrderObject.getAddress()		1, 1, 2, 0
OrderObject.getName()			1, 1, 2, 0
OrderObject.getPhoneNumber()		1, 1, 2, 0
OrderObject.OrderObject()		1, 3, 2, 0
OrderObject.OrderObject()		1, 0, 0, 0
OrderObject.setAddress()		1, 1, 2, 0
OrderObject.setName()			1, 1, 2, 0
OrderObject.setPhoneNumber()		1, 1, 2, 0
OrderPrinter.setId()			4, 19, 3, 11
OrderPrinter.setId()			1, 1, 2, 0


核心方法复杂度失控(如DangerousRateStrategy.setId()达5级复杂度,Main.setId()含48行代码),显示业务逻辑过度耦合
依旧完全缺乏代码注释,可维护性差
深度嵌套加剧(最大块深度4层)。优点体现在模块化设计雏形(不同RateStrategy实现),但过度依赖基础setter方法导致类膨胀。

踩坑心得

输入缓冲区残留导致数据读取异常

在main方法中,因为输入的东西很多,在部分地方我使用nextInt()读取整数后直接调用nextLine(),又有时候错误地多使用了nextLine(),导致我整个程序逻辑正常地情况下,因为这种细节问题,运行时发生了错误。后来我一步一步检查我的输入过程,才把错误给改正。

支付方式判断时发生错误

一开始匹配时,对于字符串的判断我并未使用equals方法,而是使用了“==”。

改进建议

输入处理优化

在上面讲到过因为输出踩了坑,我认为在输入时可以统一输入方式:所有输入先用nextLine()读取字符串,再手动转换类型(如Integer.parseInt(sc.nextLine())),避免缓冲区问题。

从字符串匹配改到枚举匹配

第二次题目中通过字符串goodsType匹配策略(如case "Normal"),若输入错误(如"norm")则策略为空,导致空指针异常。但是可以创建RateStrategyFactory类,根据枚举值动态创建策略对象则不会。

注释规范

合理的代码注释是提升项目可维护性的关键实践,而我0%注释覆盖率,可以适当进行注释,这样可以避免在看到后一次题目时,因为时间拉长而忘记第一次写的是什么,导致又要重新读题,重新读自己代码,浪费时间。通过规范的注释记录核心算法、业务规则和关键参数,既能建立代码与需求的快速映射,又能避免因人员流动导致的知识断层。这不仅节省后期维护50%以上的理解时间,更降低了因误读代码产生的二次错误风险。

总结

完成了第二次Java编程博客,这两次航空货运系统的实践让我深刻体会到面向对象编程从理论到实践的跨越。第一次程序初步搭建了类的继承体系(如Customer继承Human)和策略模式框架,但存在输入处理粗糙、策略匹配硬编码等问题,代码复杂度集中在无关的setter方法上,暴露了设计经验的不足。第二次尝试引入枚举类型(CustomerType)规范业务逻辑,扩展多策略计费规则,并通过OrderObject解耦 “人” 的强关联,明显感受到系统灵活性的提升。然而,开发中也踩了不少坑:输入缓冲区残留导致数据混乱、字符串匹配策略时的空指针风险、以及过度依赖setter/getter导致类职责膨胀等。这些经历让我意识到,面向对象的魅力不仅在于类与继承的层级美感,更在于对细节的雕琢 —— 从 Scanner 输入的顺序控制到策略模式的工厂重构,从枚举的类型安全到注释对代码可维护性的影响,每一处改进都是对 “封装变化、复用共性” 原则的深入理解。这次实践虽非大型项目,却让我在字段权限的精细化设计、方法重写的合理性校验等细节中,深刻体会到三大特性如何相互赋能:封装是根基,界定对象的独立边界;继承是脉络,构建代码的复用体系;多态是灵魂,赋予系统动态扩展的生命力。未来,我将继续以实践为镜,在更多场景中探索面向对象编程的深层智慧,也期待与读者分享更多技术成长的思考。

posted @ 2025-05-24 20:24  GallonLee  阅读(19)  评论(0)    收藏  举报