BLOG-NCHU_航空货运管理系统
NCHU_航空货运管理系统
前言
这两次关于航空货运管理系统的作业重点在于其中的类设计,要求符合面向对象程序设计原则,题目的核心逻辑较为简单,
对数据的处理也比较直接,但其难点是如何用合理的类设计来处理这些问题,最终得到理想的结果。
现在对题目本身进行一些叙述,航空货运在当今社会已经较为常见,他高效的运输速度,与强大的货运能力被许多的企业
推崇与应用,这样就会出现更多的订单,所以航空货运管理系统就显得格外重要。这两次的题目虽然只是一个简化版的管
理系统,但依旧可以体现面向对象编程技术在处理实际问题的优越性。
设计与分析
第一次题目分析
题目概述:
某航空公司“航空货运管理系统”中的空运费的计算涉及多个因素,通常包括
- 货物重量/体积
- 运输距离
- 附加费用
- 货物类型
- 客户类型
- 市场供需
本次作业主要考虑货物重量/体积
输入格式:
客户编号
客户姓名
客户电话
客户地址
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为YYYY-MM-DD)
航班最大载重量
订单编号
订单日期(格式为YYYY-MM-DD)
发件人地址
发件人姓名
发件人电话
收件人地址
收件人姓名
收件人电话
输出格式
客户:姓名(电话)订单信息如下:
-----------------------------------------
航班号:
订单号:
订单日期:
发件人姓名:
发件人电话:
发件人地址:
收件人姓名:
收件人电话:
收件人地址:
订单总重量(kg):
微信支付金额:
货物明细如下:
-----------------------------------------
明细编号 货物名称 计费重量 计费费率 应交运费
1 ...
2 ...
- 如果订单中货物重量超过
航班剩余载重量,程序输出The flight with flight number:航班号 has exceeded its load capacity and cannot carry the order.程序终止运行。
分析设计
类设计
- 接口与抽象类
Dayinable接口:定义了dayin()方法,用于打印信息。多个实体类(如 Customer、Sender、Geter 等)实现了该接口,体现了多态性。
Payable接口:定义了getall()方法,用于计算总金额。支付相关类(Wechat、Ali)实现了该接口。
Example抽象类:实现了 Dayinable 接口,作为多个实体类的父类,包含 ID 属性和相关方法。
Pay抽象类:作为支付类的父类,体现支付方式多样性。 - 数据实体类
客户类(Customer)
发件人(Sender)和收件人(Geter)
货物类(Goods)
航班类(Flight)
订单类(Order)
支付类(Wechat、Ali) - 业务逻辑处理
订单管理类(Dindan)
关键方法设计
- 计费重量计算
public double realweight(){
double rw=(w*l*h)/6000;
if(rw>weight){
return rw;
}
else return weight;
}
在本题中,计费重量不一定与实际重量相同,它等于实际重量,与体积重量的较高值,用此方法进行对计费重量的计算。
(其中体积重量的计算公式为:体积重量(kg)= 货物体积(长×宽×高,单位:厘米)÷6000)
- 费率的计算
public double rate(){
if(realweight()<20){
return 35.0;
}
else if(realweight()>=20&&realweight()<50){
return 30.0;
}
else if(realweight()>=50&&realweight()<100){
return 25.0;
}
else return 15.0;
}
本题中不同的计费重量所对应的费率是不同的,他与计费重量的关系如下图:

用上面的方法求取实际的费率,用于计算实际运费。
- 订单信息展示方法
public void show(){
customer.dayin();
System.out.println("-----------------------------------------");
flight.dayin();
order.dayin();
sender.dayin();
geter.dayin();
System.out.printf("订单总重量(kg):%.1f\n", order.getall());
wechat.dayin();
System.out.print("\n");
System.out.println("货物明细如下:");
System.out.println("-----------------------------------------");
System.out.printf("明细编号\t货物名称\t计费重量\t计费费率\t应交运费\n");
int i=1;
while(i<=order.getList().size()){
System.out.print(i+"\t");
order.getList().get(i-1).dayin();
i++;
}
}
本方法通过调用各个类的Dayin()方法,实现了对订单信息的展示,匹配了题目给出格式,得到正确的结果。
设计总结与分析
- 面向对象设计原则的应用总结
抽象与多态:通过Dayinable接口和Example抽象类,实现了不同实例(客户、货物、航班等)的统一打印,提高了代码复用性。
接口隔离:Payable接口将计算总金额的行为抽象出来,使支付方式(微信、支付宝)具有扩展性
单一职责原则:核心计费规则将复杂的运费计算逻辑(体积重量、阶梯费率)封装在Goods类中
- 缺点分析
在本次题目中,代码有大量的冗杂重复,如Customer、Sender、Geter类中有大量重复的属性(name、call、address),未抽象出公共父类,
同时在Wechat和Ali类中的getall()方法实现完全相同,未提取到父类Pay中,这些造成了代码的可读性大大下降,但在我下次的题目中,我做出了
一定的改进,使代码更加的精简。
SourceMonitor分析

从图中可见平均方法数为10,体现类中实现的方法过多,同时也缺少注释,代码可读性较低,其中平均复杂度与最大复杂度为异常数据(我也不知道为什么),
实际平均复杂度约为 1.5-2,最高复杂度为 5(用其他工具测出),总体复杂度较为正常。
类图

类图中体现了各个类之间的关系
第二次题目分析
题目概述
本次题目与上次题目大体相同,但是依旧有一定的改变,改变如下:
- 订单类型增加:新增由普通(Normal)、加急(Expedite)、危险品(Dangerous)类型,不同类型对应的费率也会有所改变(具体改变下面有提)
- 支付方式改变:可以支持不同的支付方式(在输入格式有体现)
- 客户类型区分:区分个人和企业,分别享受9折和8折的优惠
输入格式
客户类型[可输入项:Individual/Corporate]
客户编号
客户姓名
客户电话
客户地址
货物类型[可输入项:Normal/Expedite/Dangerous]
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为YYYY-MM-DD)
航班最大载重量
订单编号
订单日期(格式为YYYY-MM-DD)
发件人地址
发件人姓名
发件人电话
收件人地址
收件人姓名
收件人电话
支付方式[可输入项:Wechat/ALiPay/Cash]
输出格式
客户:姓名(电话)订单信息如下:
-----------------------------------------
航班号:
订单号:
订单日期:
发件人姓名:
发件人电话:
发件人地址:
收件人姓名:
收件人电话:
收件人地址:
订单总重量(kg):
[微信/支付宝/现金]支付金额:
货物明细如下:
-----------------------------------------
明细编号 货物名称 计费重量 计费费率 应交运费
1 ...
2 ...
- 如果订单中货物重量超过航班剩余载重量,
程序输出The flight with flight number:航班号 has exceeded its load capacity and cannot carry the order.程序终止运行。
分析设计
类设计
本次的类设计相较上一次有所改变,改变如下:
-
Goods类的抽象化与子类分化
Goods被设计为抽象类,将通用的属性和方法(如realweight()、getmoney()、dayin())抽象出来,
同时分有Normalgoods、Expeditegoods、Dangerousgoods 等具体子类继承,每个子类实现各自的rate()方法,
实现不同类型货物费率计算的差异化 -
支付类的调整
Pay抽象类增加了Example customer,在计算总费用时会根据客户类型给予不同折扣 ,
Wechat、Ali、Cash(新增)等支付子类继承该方法,以满足不同客户的优惠策略。 -
新增Orderall类
之前订单相关订单信息统计和展示逻辑在Order类中,现在将货物列表管理、总重量计算、货物明细展示等功能提取到Orderall类中,
使Order类职责更单一,专注于订单整体信息的展示,提高了代码的内聚性。
关键方法的调整
- rate()方法的更改
新的运算逻辑:

因为不同的货物类型有不同的计算方法,所以这个方法在子类中分别实现,以下是Normalgoods的对应方法:
public double rate(){
if(realweight()<20){
return 35.0;
}
else if(realweight()>=20&&realweight()<50){
return 30.0;
}
else if(realweight()>=50&&realweight()<100){
return 25.0;
}
else return 15.0;
}
- 支付金额计算方法更改
由于不同用户的折扣力度不同,所以计算运费的方法也要进行修正:
public double getall(){
double count=0;
for(int i=0;i<orderall.getList().size();i++){
count+=orderall.getList().get(i).getmoney();
}
if(customer.kind.equals("Individual")){
return 0.9*count;
}
else{
return 0.8*count;
}
}
除此之外其他部分有些许改变,但是大体不变,就不过多赘述。
设计总结与分析
-
类结构优化
将Goods类抽象化,衍生出Normalgoods、Expeditegoods、Dangerousgoods 等子类,使不同类型货物的费率计算规则各自独立实现 -
职责分离
新增Orderall类,将货物统计、总重量计算、货物展示等功能从Order类分离出来,
这使得Order类专注于订单整体信息展示,Orderall类专注于对货物操作。 -
仍存在的不足
命名采用拼音的形式,这样不规范,对代码的可读性造成一定的影响,同时部分子类在一些逻辑上
有相似之处,可以都抽象在父类中,使代码更加精简。
SourceMonitor分析

代码的语句数,分支数,深度都有所提升,这是因为题目复杂度提升(也办法啊),由分支数判断复杂度的数据依旧是错误的,
实际的复杂度为:最高复杂度为5,平均复杂度为1.5-2。总体上与第一次差不多。
类图

以上就为代码中类的关系
踩坑心得
关于换行符的吸收问题

这是由于在使用连续nextDouble()和nextLine()时,需用in.nextLine()吸收换行符,否则会导致数据与类型不匹配。
关于输出结构问题
这个坑纯属是失误,我以为输出货物详细信息使用空格分开,但是答案是错误的(改了我好久)

最后改了不下10几遍终于发现错误是分开是用 \t ,我真的服了.....
改进建议
-
命名规范
统一中英文命名:将拼音命名(如 dayin())改为英文(如 show()) -
消除代码冗杂
将Wechat、Ali、Cash中的重复代码移至父类 Pay
新建Person类,将Customer、Sender、Receiver 中的公共属性可放至Person -
异常数据处理
添加异常数据的处理与判断,使程序更加通用合理
总结
通过这两次的题目,我对面向对象程序设计原则有了更加深刻的理解。通过运用这些原则,使我的航空货运管理系统逻辑更加清晰,
代码更有条理,对后面的扩展性有极大的帮助,同时这次的题目集新加了抽象与接口,他们为后面众多类的管理起了巨大作用。
这次的两次作业同时也暴露了我许多的问题,如刚开始对接口意义理解不清,导致开始的设计逻辑有巨大错误,但在做题的过程中,
我慢慢体会到了接口的作用,这对我的编程能力有了巨大的提升
浙公网安备 33010602011771号