第二次blog作业 航空货运管理系统分析

一.前言

这算是第二次大作业了,并且上周进行了期中考试,让我真正的认清自己,并且有了上次的经验,我这次开始的时间还是比较早的。下面是第二次大作业的分析

知识点

1.首先是对面向对象设计原则的考察
单一职责原则 每个类只负责一个功能
里氏代换原则 子类可以替换父类而不影响程序
开闭原则 对扩展开放,对修改关闭
合成复用原则 优先使用组合而非继承
依赖倒转原则 依赖抽象,而非具体实现
2.然后就是对输入输出字符串的考察
3.还有就是java的基本语法
Java容器类的使用
Java的继承与多态

题量和难度

题量完美的展现出来了大作业的特点,别看我题少,可你就是写不出来,但是这次题目集与上一次相比我感觉难度还是降了不少的,这次的主要难度并不在逻辑分析或者计算上面,主要是在对题目的理解及类的划分,但是由于我之前的偷懒,所以对我来说,其实难度还是不少的。

二.设计与分析

第一次题目的设计与分析

题目要求

某航空公司“航空货运管理系统”中的空运费的计算涉及多个因素,通常包
括货物重量/体积、运输距离、附加费用、货物类型、客户类型以及市场供需等。
本次作业主要考虑货物重量/体积,以下是具体的计算方式和关键要点:
一、计费重量的确定
空运以实际重量(GrossWeight)和体积重量(VolumeWeight)中的较
高者作为计费重量。
计算公式:
体积重量(kg)= 货物体积(长×宽×高,单位:厘米)÷6000
示例:
若货物实际重量为80kg,体积为120cm×80cm×60cm,则:
体积重量 =(120×80×60)÷6000=96kg
计费重量取96kg(因96kg>80kg)。
二、基础运费计算
费率(Rate):航空公司或货代根据航线、货物类型、市场行情等制定(如
CNY 30/kg)。本次作业费率采用分段计算方式:
公式:基础运费 = 计费重量 × 费率
三、题目说明
本次题目模拟某客户到该航空公司办理一次货运业务的过程:
航空公司提供如下信息:
航班信息(航班号,航班起飞机场所在城市,航班降落机场所在城市,航班
日期,航班最大载重量)
客户填写货运订单并进行支付,需要提供如下信息:
 客户信息(姓名,电话号码等)
 货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)
 运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选
航班号,订单日期)
 支付方式(支付宝支付、微信支付)
注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独
计费。
程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信
息报表及货物明细报表。
四、题目要求
本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开
闭原则以及合成复用原则
输入格式:
按如下顺序分别输入客户信息、货物信息、航班信息以及订单信息。
客户编号
客户姓名
客户电话
客户地址
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为YYYY-MM-DD)
航班最大载重量
订单编号
订单日期(格式为YYYY-MM-DD)
发件人地址
发件人姓名
发件人电话
收件人地址
收件人姓名
收件人电话
输出格式:
如果订单中货物重量超过航班剩余载重量,程序输出The flight with flight number:航班号 has exceeded its load capacity and cannot carry the order. ,程序终止运行。
如果航班载重量可以承接该订单,输出如下:
客户:姓名(电话)订单信息如下:

航班号:
订单号:
订单日期:
发件人姓名:
发件人电话:
发件人地址:
收件人姓名:
收件人电话:
收件人地址:
订单总重量(kg):
微信支付金额:

货物明细如下:

明细编号 货物名称 计费重量 计费费率 应交运费
1 ...
2 ...
注:输出中实型数均保留1位小数。

输入格式:
按如下顺序分别输入客户信息、货物信息、航班信息以及订单信息。
客户编号
客户姓名
客户电话
客户地址
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为YYYY-MM-DD)
航班最大载重量
订单编号
订单日期(格式为YYYY-MM-DD)
发件人地址
发件人姓名
发件人电话
收件人地址
收件人姓名
收件人电话
输出格式:
如果订单中货物重量超过航班剩余载重量,程序输出The flight with flight number:航班号 has exceeded its load capacity and cannot carry the order. ,程序终止运行。
如果航班载重量可以承接该订单,输出如下:
客户:姓名(电话)订单信息如下:

航班号:
订单号:
订单日期:
发件人姓名:
发件人电话:
发件人地址:
收件人姓名:
收件人电话:
收件人地址:
订单总重量(kg):
微信支付金额:

货物明细如下:

明细编号 货物名称 计费重量 计费费率 应交运费
1 ...
2 ...
注:输出中实型数均保留1位小数。

输入样例:
在这里给出一组输入。例如:

10001
郭靖
13807911234
南昌航空大学
2
101
发电机
80
60
40
80
102
信号发生器
55
70
60
45
MU1234
昌北国际机场
大兴国际机场
2025-04-22
1000
900001
2025-04-22
南昌大学
洪七公
18907912325
北京大学
黄药师
13607912546
输出样例:
在这里给出相应的输出。例如:

客户:郭靖(13807911234)订单信息如下:

航班号:MU1234
订单号:900001
订单日期:2025-04-22
发件人姓名:洪七公
发件人电话:18907912325
发件人地址:南昌大学
收件人姓名:黄药师
收件人电话:13607912546
收件人地址:北京大学
订单总重量(kg):125.0
微信支付金额:3350.0

货物明细如下:

明细编号 货物名称 计费重量 计费费率 应交运费
1 发电机 80.0 25.0 2000.0
2 信号发生器 45.0 30.0 1350.0

题目类图

类的设计

(1) Product 类(货物信息)
属性:
productID:货物编号
name:货物名称
width/length/height:货物尺寸(厘米)
weight:货物实际重量(kg)
方法:
Product(int, String, double, double, double, double) 构造函数,初始化货物信息
double calculateVolumeWeight() 计算体积重量 = (长×宽×高) / 6000
double getChargeableWeight() 返回计费重量(实际重量与体积重量的较大值)
String getName() 获取货物名称
double getWeight() 获取实际重量
(2) Flight 类(航班信息)
属性:
flightNumber:航班号
departureAirport/arrivalAirport:起降机场
flightDate:航班日期
maxLoadCapacity:最大载重量(kg)
方法:
Flight(String, String, String, String, double) 构造函数,初始化航班信息
String getFlightNumber() 获取航班号
double getMaxLoadCapacity() 获取最大载重量
(3) OrderItem 类(订单项)
属性:
itemID:明细编号
product:关联的 Product 对象
方法:
OrderItem(int, Product) 构造函数,绑定货物和明细编号
double getChargeableWeight() 调用 Product 的计费重量计算
double getRate() 根据重量分段返回费率)
double calculateFreight() 计算运费 = 计费重量 × 费率
String getProductName() 获取货物名称
(4) Order 类(订单管理)
属性:
orderID:订单编号
orderDate:订单日期
senderName/senderPhone/senderAddress:发件人信息
recipientName/recipientPhone/recipientAddress:收件人信息
orderItems:订单项列表(ArrayList)
方法:
Order(int, String, String, String, String, String, String, String) 构造函数,初始化订单基本信息
void addOrderItem(OrderItem) 添加订单项
double calculateTotalWeight() 计算订单总重量(所有货物计费重量之和)
double calculateWechatPayment() 计算微信支付总金额(所有货物运费之和)
int getOrderID() 获取订单编号
String getOrderDate() 获取订单日期
String getSenderName()/getSenderPhone()/getSenderAddress() 获取发件人信息
String getRecipientName()/getRecipientPhone()/getRecipientAddress() 获取收件人信息
ArrayList getOrderItems() 获取订单项列表
(5) Customer 类(客户信息)
属性:
customerID:客户编号
name:客户姓名
phone:客户电话
address:客户地址
方法:
Customer(int, String, String, String) 构造函数,初始化客户信息
String getName() 获取客户姓名
String getPhone() 获取客户电话
(6) Main 类(程序入口)
方法:
public static void main(String[] args) 主方法,处理输入、业务逻辑和输出

报表

报表分析

行数:488 行
语句数:366 条语句
分支语句百分比:5.5%
方法调用语句数:116
类和接口总数:12
每类平均方法数:5.00
每方法平均语句数:3.97
最复杂方法所在行号:66
最复杂方法名称:Main.main()
最大复杂度:5
最深嵌套块所在行号:69
最大嵌套深度:3
平均嵌套深度:1.72
平均复杂度:1.33

踩坑与心得

这次大作业之前已经说过了,难度并不算大,主要就是在类的设计和职责划分上面,我原本并没有考虑到订单项类,订单类,只考虑了商品类,然后将商品类设为一个数组构成订单,这样发现并不合理,因为这违背了开闭原则,并且在数组的设定上面花费了好多时间,后来老师上课讲了类的设计,我改了订单项类和订单类。
在输入处理上存在问题,输入处理:使用Scanner按顺序读取输入数据,测试用例大量存在换行符注意处理换行符(scanner.nextLine())。

改进


参考上述老师的类图我改进了我的类图

输出格式化:使用printf按指定格式输出,提升可读性(如保留一位小数)

第二次题目的设计与分析

题目要求

某航空公司“航空货运管理系统”中的空运费的计算涉及多个因素,通常包
括货物重量/体积、运输距离、附加费用、货物类型、客户类型以及市场供需等。
本次作业主要考虑货物重量/体积,以下是具体的计算方式和关键要点:
一、计费重量的确定
空运以实际重量(GrossWeight)和体积重量(VolumeWeight)中的较
高者作为计费重量。
计算公式:
体积重量(kg)= 货物体积(长×宽×高,单位:厘米)÷6000
示例:
若货物实际重量为80kg,体积为120cm×80cm×60cm,则:
体积重量 =(120×80×60)÷6000=96kg
计费重量取96kg(因96kg>80kg)。
二、基础运费计算
1
费率(Rate):航空公司或货代根据航线、货物类型、市场行情等制定(如
CNY30/kg)。本次作业费率与货物类型有关,货物类型分为普通货物、危险货
物和加急货物三种,其费率分别为:
计算公式:基础运费 = 计费重量 × 费率 × 折扣率
其中,折扣率是指不同的用户类型针对每个订单的运费可以享受相应的折扣,
在本题中,用户分为个人用户和集团用户,其中个人用户可享受订单运费的9
折优惠,集团用户可享受订单运费的8折优惠。
三、题目说明
本次题目模拟某客户到该航空公司办理一次货运业务的过程:
航空公司提供如下信息:
航班信息(航班号,航班起飞机场,航班降落机场,航班日期,航班最大载
重量)
2
客户填写货运订单并进行支付,需要提供如下信息:
 客户信息(姓名,电话号码等)
 货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)
 运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选
航班号,订单日期)
 支付方式(支付宝支付、微信支付、现金支付)
注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独
计费。
程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信
息报表及货物明细报表。
四、题目要求
本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开
闭原则以及合成复用原则、依赖倒转原则,除需要在PTA平台提交源码外,还
需要在超星平台提交本次作业最终得分源码(首次提交最高分源码)的类图,
设计因素:单一职责原则(20%)、里氏代换原则(20%)、开闭原则(20%)、
合成复用原则(20%)、依赖倒转原则(20%)。
提醒:本题需求可扩展的类:用户、支付方式、货物
输入格式:
按如下顺序分别输入客户信息、货物信息、航班信息以及订单信息。
客户类型[可输入项:Individual/Corporate]
客户编号
客户姓名
客户电话
客户地址
货物类型[可输入项:Normal/Expedite/Dangerous]
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为YYYY-MM-DD)
航班最大载重量
订单编号
订单日期(格式为YYYY-MM-DD)
发件人地址
发件人姓名
发件人电话
收件人地址
收件人姓名
收件人电话
支付方式[可输入项:Wechat/ALiPay/Cash]
输出格式:
如果订单中货物重量超过航班剩余载重量,程序输出The flight with flight number:航班号 has exceeded its load capacity and cannot carry the order. ,程序终止运行。
如果航班载重量可以承接该订单,输出如下:
客户:姓名(电话)订单信息如下:

航班号:
订单号:
订单日期:
发件人姓名:
发件人电话:
发件人地址:
收件人姓名:
收件人电话:
收件人地址:
订单总重量(kg):
[微信/支付宝/现金]支付金额:
货物明细如下:

明细编号 货物名称 计费重量 计费费率 应交运费
1 ...
2 ...
注:输出中实型数均保留1位小数。
输入样例:
在这里给出一组输入。例如:
Corporate
10001
郭靖
13807911234
南昌航空大学
Expedite
2
101
发电机
80
60
40
80
102
信号发生器
55
70
60
45
MU1234
昌北国际机场
大兴国际机场
2025-04-22
1000
900001
2025-04-22
南昌大学
洪七公
18907912325
北京大学
黄药师
13607912546
ALiPay
输出样例:
在这里给出相应的输出。例如:
客户:郭靖(13807911234)订单信息如下:

航班号:MU1234
订单号:900001
订单日期:2025-04-22
发件人姓名:洪七公
发件人电话:18907912325
发件人地址:南昌大学
收件人姓名:黄药师
收件人电话:13607912546
收件人地址:北京大学
订单总重量(kg):125.0
支付宝支付金额:4360.0
货物明细如下:

明细编号 货物名称 计费重量 计费费率 应交运费
1 发电机 80.0 40.0 3200.0
2 信号发生器 45.0 50.0 2250.0

题目类图

类的设计

Customer类 客户信息管理
方法:
Customer(String type, int id, String name, String phone, String address) 构造函数
String getType() 获取客户类型(Individual/Corporate)
int getId() 获取客户ID
String getName() 获取客户姓名
String getPhone() 获取客户电话
String getAddress() 获取客户地址
Product类 货物信息管理
方法:
Product(int id, String name, double length, double width, double height, double weight, String type) 构造函数
double getVolumeWeight() 计算体积重量(长×宽×高÷6000)
double getChargeableWeight() 获取计费重量(实际重量和体积重量的较大值)
String getName() 获取货物名称
String getType() 获取货物类型(Normal/Dangerous/Expedite)
double getWeight() 获取实际重量
Flight类 航班信息管理
方法:
Flight(String number, String departure, String arrival, String date, double maxLoad) 构造函数
String getNumber() 获取航班号
String getDeparture() 获取起飞机场
String getArrival() 获取降落机场
String getDate() 获取航班日期
double getMaxLoad() 获取最大载重量
OrderItem0类 订单项管理
方法:
OrderItem(int id, Product product) 构造函数
double getRate(String type, double weight) 根据货物类型和重量计算费率(含分段逻辑)
int getId() 获取明细编号
String getProductName() 获取货物名称
double getChargeableWeight() 获取计费重量
double getRate() 获取费率
double getFreight() 计算运费(计费重量×费率)
Order类 订单管理
方法:
Order(String id, String date, String senderName, String senderPhone, String senderAddress, String recipientName, String recipientPhone, String recipientAddress, String paymentMethod, Customer customer, Flight flight) 构造函数
void addItem(OrderItem item) 添加订单项
double getTotalWeight() 计算订单总重量
double getTotalFreight() 计算总运费(含客户折扣)
void printOrder() 打印订单信息(含货物明细)
Main类 程序入口
方法:
public static void main(String[] args)

报表

报表分析

行数:328 行
语句数:174 条语句
分支语句百分比:11.5%
方法调用语句数:18
类和接口总数:5
每类平均方法数:6.00
每方法平均语句数:3.43
最复杂方法所在行号:131
最复杂方法名称:OrderItem.getRate()
最大复杂度:14
最深嵌套块所在行号:135
最大嵌套深度:5
平均嵌套深度:1.95
平均复杂度:1.55

踩坑与心得

在上次基础上新加了继承与多态,基本上没什么大问题,唯一不合理点是我将费率设计为单独的类,后来在代码分析时发现与上一次作业类设计不符合,于是又将费率的分类讨论设在订单项类中

改进

三.总结

1.通过这次大作业主要学到了代码设计的几个原则
单一职责原则
将Order类拆分为:
OrderCreator:负责订单创建 PriceCalculator:处理价格计算 InventoryUpdater:管理库存更新 LogisticsTracker:跟踪物流状态
这种拆分使每个类的职责单一明确,修改价格计算逻辑时,只需调整PriceCalculator类,不会影响其他功能,大大降低了维护成本。
里氏代换原则
设计了Payment基类,然后派生出CreditCardPayment、AlipayPayment等子类。最初某个子类重写了父类的validate方法时完全改变了验证逻辑,导致系统在处理该支付方式时出现异常,添加新的支付方式(如新增WeChatPayPayment)变得非常简单,只需继承Payment基类并实现必要方法,无需修改现有代码。
开闭原则
这种设计在新增CSV报表支持时需要修改原有代码。通过应用OCP原则,我们引入ReportGenerator接口和多种实现类(PDFGenerator、ExcelGenerator等),然后使用工厂模式创建适当的生成器。现在新增报表类型只需添加新的实现类,无需修改现有代码,将变化的部分封装起来,使系统对扩展开放,对修改关闭。
合成复用原则
当需要添加新的用户特性时,需要在所有相关类中添加重复代码。
依赖倒转原则
高层模块不应依赖低层模块,二者都应依赖于抽象
抽象不应依赖细节,细节应该依赖抽象
2.还主要学习了工厂模式
工厂模式(Factory Pattern)是创建型设计模式中最常用的一种,它提供了一种创建对象的最佳方式,将对象的创建与使用分离。工厂模式的核心思想是定义一个创建对象的接口,但让子类决定实例化哪个类。
课堂上主要讲了new新对象时
传统方式的问题:Car car = new BMW(); 代码直接依赖具体类,违反开闭原则
工厂模式解决方案:Car car = CarFactory.createCar("BMW");只与工厂接口交互,无需知道具体实现类

posted @ 2025-05-25 22:40  沉溺于世界  阅读(42)  评论(0)    收藏  举报