第二次Blog作业
第二次Blog作业:JAVA航空货运管理系统
一、前言
经历了电梯难题后,pta作业水温明显下降,两次作业题目的难点不在于算法设计,而是在于类的设计,换句话说就是按照给的类图完成需求的实现,只要读懂题意,读懂需求,就可以轻松完成任务。
大部分题目都是要求重构,在原有的基础上完成继承与多态,基本不涉及算法,只要求处理好类与类的关系即可。
二、点线面问题
题目集08 点线面问题继承与多态
在“点与线(类设计)”题目基础上,对题目的类设计进行重构,以实现继承与多态的技术性需求。
对题目中的点Point类和线Line类进行进一步抽象,定义一个两个类的共同父类Element(抽象类)
将display()方法在该方法中进行声明(抽象方法),将Point类和Line类作为该类的子类。
再定义一个Element类的子类面Plane,该类只有一个私有属性颜色color,除了构造方法和属性的getter、setter方法外,display()方法用于输出面的颜色,输出格式如下:The Plane's color is:颜色
在主方法内,定义两个Point(线段的起点和终点)对象、一个Line对象和一个Plane对象,依次从键盘输入两个Point对象的起点、终点坐标和颜色值(Line对象和Plane对象颜色相同),然后定义一个Element类的引用,分别使用该引用调用以上四个对象的display()方法,从而实现多态特性
element = p1;//起点
Point element.display();
element = p2;//终点
Point element.display();
element = line;//线段
element.display();
element = plane;//面
element.display();
设计思路
将element作为抽象父类,line、point、plane作为子类,按部就班完成设计,完成输出即可。
题目集09 点线面问题再重构(容器类)
在“点与线(继承与多态)”题目基础上,对题目的类设计进行重构,增加容器类保存点、线、面对象,并对该容器进行相应增、删、遍历操作。
在原有类设计的基础上,增加一个GeometryObject容器类,其属性为ArrayList<Element>类型的对象(若不了解泛型,可以不使用<Element>)
增加该类的add()方法及remove(int index)方法,其功能分别为向容器中增加对象及删除第index - 1(ArrayList中index>=0)个对象
-在主方法中,用户循环输入要进行的操作(choice∈[0,4]),其含义如下:
- 1:向容器中增加Point对象
- 2:向容器中增加Line对象
- 3:向容器中增加Plane对象
- 4:删除容器中第index - 1个数据,若index数据非法,则无视此操作
- 0:输入结束
示例代码如下:
choice = input.nextInt();
while(choice != 0) {
switch(choice) {
case 1://insert Point object into list
...
break;
case 2://insert Line object into list
...
break;
case 3://insert Plane object into list
...
break;
case 4://delete index - 1 object from list
int index = input.nextInt();
...
}
choice = input.nextInt();
}
设计思路
- 指令1、2、3,用add方法即可
obj.addElement(point);
obj.addElement(line);
obj.addElement(plane);
- 指令4用remove方法
obj.remove(index);
其他基本和题目集08一致
三、航空货运管理系统
题目集08 航空货运管理系统(类设计)
题目:
某航空公司“航空货运管理系统”中的空运费的计算涉及多个因素,通常包
括货物重量/体积、运输距离、附加费用、货物类型、客户类型以及市场供需等。
本次题目模拟某客户到该航空公司办理一次货运业务的过程:
航空公司提供如下信息:
航班信息(航班号,航班起飞机场所在城市,航班降落机场所在城市,航班
日期,航班最大载重量)
客户填写货运订单并进行支付,需要提供如下信息:
客户信息(姓名,电话号码等)
货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)
运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选
航班号,订单日期)
支付方式(支付宝支付、微信支付)
要求:
一、计费重量的确定
空运以实际重量(Gross Weight)和体积重量(Volume Weight)中的较
高者作为计费重量。
计算公式:
体积重量(kg) = 货物体积(长×宽×高,单位:厘米)÷ 6000
示例:
若货物实际重量为 80kg,体积为 120cm×80cm×60cm,则:
体积重量 = (120×80×60) ÷ 6000 = 96kg
计费重量取 96kg(因 96kg > 80kg)。
二、基础运费计算
费率(Rate):航空公司或货代根据航线、货物类型、市场行情等制定(如
CNY 30/kg)。本次作业费率采用分段计算方式:
a1
公式:基础运费 = 计费重量 × 费率
三、题目说明
注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独
计费。
程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信
息报表及货物明细报表。
如果订单中货物重量超过航班剩余载重量,程序输出The flight with flight number:航班号 has exceeded its load capacity and cannot carry the order. ,程序终止运行。
输出结果保留一位小数。
输入格式:
客户编号
客户姓名
客户电话
客户地址
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为YYYY-MM-DD)
航班最大载重量
订单编号
订单日期(格式为YYYY-MM-DD)
发件人地址
发件人姓名
发件人电话
收件人地址
收件人姓名
收件人电话
输出格式:
客户:姓名(电话)订单信息如下:
-----------------------------------------
航班号:
订单号:
订单日期:
发件人姓名:
发件人电话:
发件人地址:
收件人姓名:
收件人电话:
收件人地址:
订单总重量(kg):
微信支付金额:
货物明细如下:
-----------------------------------------
明细编号 货物名称 计费重量 计费费率 应交运费
1 ...
2 ...
设计思路
- 其实这道题并不难,难点在于真正读懂题目的人。由题目易得大部分输出的内容都是输入的信息,真正要算的其实并没多少。要计算订单总重量、微信支付金额、货物明细(要计算计费重量 、 计费费率 、 应交运费)。
- 首先要明白题目的底层逻辑,一些基础概念,如:
计费重量:
空运以实际重量(Gross Weight)和体积重量(Volume Weight)中的较 高者作为计费重量
体积重量:
体积重量(kg) = 货物体积(长×宽×高,单位:厘米)÷ 6000
费率(Rate):(该重量是指实际重量)

getRate方法:
public double getRate() {
if (this.getChargeableWeight() < (double)20.0F) {
return (double)35.0F;
} else if (this.getChargeableWeight() < (double)50.0F) {
return (double)30.0F;
} else {
return this.getChargeableWeight() < (double)100.0F ? (double)25.0F : (double)15.0F;
}
}
基础运费
基础运费 = 计费重量 × 费率
-
然后再求订单总重量、微信支付金额。
-
关于类的设计,首先是有顾客类、收件人类、寄件人类、货物类、飞行信息类,根据职责单一原则像计费重量、体积重量、费率这种货物本身的属性就放在货物类,而计算订单总重量以及所需支付的金额需要增加一个计算类以及订单项类。
分析
SOURCEMONITOR
- 规模:333 行,202 条语句,8 个类,50 个方法
- 结构:方法平均 2.5 条语句(优秀),分支语句仅 4.5%
- 复杂度:平均 1.18,最高
Main.main()(5),深度≤4

POWERDESIGN
- Person:抽象类,拥有
name(姓名) 、telephoneNumber(电话号码)、address(地址)属性,有构造方法及对应属性的 Getter 和 Setter 方法。它是Receiver(收件人)、Customer(客户)、Sender(寄件人)的父类或具有相似结构的基类。 - Receiver:继承自
Person(从关系推测) ,构造方法接收name、telephoneNumber、address参数,与其他类在同一文件(<<sameFile>>表示) 。 - Order:表示订单,有
orderNumber(订单号)、orderDate(订单日期)、goodsList(商品列表)属性 。有构造方法,以及对这些属性的 Getter 和 Setter 方法,还有添加商品到列表addList、获取总重量getTotalWeight、获取总价格getTotalPrice方法。 - Goods:表示商品,属性包括
goodsNum(商品编号)、name(商品名)、length(长)、width(宽)、height(高)、weight(重量) 。有构造方法和对应属性的 Getter、Setter 方法,还有计算体积重量getVolumeWeight、计费重量getChargeableWeight、价格getPrice等业务方法。 - Flight:表示航班,属性有
flightNumber(航班号)、departureAirport(出发机场)、arrivalAirport(到达机场)、flightDate(航班日期)、maxLoad(最大载重) 。构造方法接收相关参数,有判断能否承载重量的canCarry方法,以及各属性的 Getter 和 Setter 方法。 - Customer:继承自
Person,构造方法接收name、telephoneNumber、address参数 ,与其他类在同一文件。 - Sender:继承自
Person,构造方法接收name、telephoneNumber、address参数 ,与其他类在同一文件。


总结
- 核心类:抽象类
Person含姓名、电话、地址属性及对应方法;Goods描述商品信息及业务方法;Flight表示航班信息及承载判断等功能。 - 业务类:
Order用于订单管理,有订单号、日期及商品列表等属性和操作方法;Customer、Sender、Receiver与Person相关,处理客户、收发件人信息。 - 程序入口:
Main类含main方法。类间通过<<sameFile>>关联,整体可用于搭建物流
题目集09 航空货运管理系统(继承与多态)
一、计费重量的确定 空运以实际重量(Gross Weight)和体积重量(Volume Weight)中的较 高者作为计费重量
二、基础运费计算 2 费率(Rate):航空公司或货代根据航线、货物类型、市场行情等制定(如 CNY 30/kg)。本次作业费率与货物类型有关,货物类型分为普通货物、危险货 物和加急货物三种
计算公式:基础运费 = 计费重量 × 费率 × 折扣率 其中,折扣率是指不同的用户类型针对每个订单的运费可以享受相应的折扣, 在本题中,用户分为个人用户和集团用户,其中个人用户可享受订单运费的 9 折优惠,集团用户可享受订单运费的 8 折优惠。
三、题目说明 本次题目模拟某客户到该航空公司办理一次货运业务的过程: 航空公司提供如下信息: 航班信息(航班号,航班起飞机场,航班降落机场,航班日期,航班最大载 重量) 3 客户填写货运订单并进行支付,需要提供如下信息: 客户信息(姓名,电话号码等) 货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等) 运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选 航班号,订单日期) 支付方式(支付宝支付、微信支付、现金支付) 注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独 计费。 程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信 息报表及货物明细报表。
四、题目要求 本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开 闭原则以及合成复用原则、依赖倒转原则,除需要在 PTA 平台提交源码外,还 需要在超星平台提交本次作业最终得分源码(首次提交最高分源码)的类图, 评判标准为: 基础得分:PTA 实际得分 设计因素:单一职责原则(20%)、里氏代换原则(20%)、开闭原则(20%)、 合成复用原则(20%)、依赖倒转原则(20%)。 最终得分:基础得分扣减所有违背设计原则分值(违背所有五个原则的设计 最终得分为 0 分) 注:提交源码时务必慎重,一定核查是否符合本次题目考核的四个设计原 则,否则一旦提交得到满分则无法修改。(文中红色字体部分为在上次作业基 础上新增的要求) 提醒:本题需求可扩展的类:用户、支付方式、货物.
输入格式:
按如下顺序分别输入客户信息、货物信息、航班信息以及订单信息。
客户类型[可输入项: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 ...
设计思路
-
这次迭代和上次的区别在于只需要扩展三个类,其余的像算法、类的设计、类的关系并没什么大的变化。
-
首先是货物类的扩展,货物类型分为普通货物、危险货 物和加急货物三种,三者有着总自己的费率求法,将货物类作为抽象类,三者作为其子类,抽象方法getRate(),在其子类中分别实现。
![]()
-
其次是支付方式的扩展,同理增加抽象类,三个子类中分别实现抽象方法getName()
-
最后是用户的扩展,这是为了之后计算所需支付的金额。计算类将用户类与订单类关联,求得最终所需支付的金额。
计算类:
class Calculate {
public Calculate() {
}
public double getTotalWeight(Order order) {
double totalWeight = (double)0.0F;
for(Goods goods : order.getGoodsList()) {
totalWeight += goods.getChargeableWeight();
}
return totalWeight;
}
public double getTotalPrice(Order order, Pay pay, Customer customer) {
double price = (double)0.0F;
for(Goods goods : order.getGoodsList()) {
price += pay.getPrice(goods, customer);
}
return price;
}
public double getPrice(Goods goods, Customer customer) {
return goods.getChargeableWeight() * goods.getRate() * customer.rate();
}
}
- 关于订单类,运用ArrayList作为列表,每一项就是一个订单,每一个订单中有不同货物,即有很多订单项构成。
ArrayList<Goods> goodsList = new ArrayList();
分析
SOURCEMONITOR


- 规模:行数 479,语句 213 条,16 个类和接口) ,方法平均语句数 1.55,规模适中但类方法分布较分散。
- 结构:分支语句占 5.2%,逻辑较简单;含注释行占比 0.0%,注释严重不足。
- 复杂度:平均复杂度 1.17,整体低;最复杂方法
ExpediteGoods.getRate()(复杂度 4 ),与前期高复杂方法相比大幅降低
POWERDESIGN


- Person:抽象类,有
name、telephoneNumber、address属性及相应的 Getter 和 Setter 方法,是Receiver、Sender、Customer等类的父类 。 - Customer:抽象类,继承自
Person,有构造方法和rate()抽象方法,CorporateCustomer(企业客户)和IndividualCustomer(个人客户)继承它并实现rate()方法 。 - Pay:抽象类,有
name属性、构造方法及getPrice(Goods goods, Customer customer)抽象方法 ,AliPay、WechatPay、CashPay继承它并实现相关方法 。 - Goods:抽象类,含
goodsNum、name、length等属性及对应 Getter、Setter 方法,还有getRate()等业务方法 。NormalGoods(普通商品)、ExpediteGoods(加急商品)、DangerousGoods(危险商品)继承它并实现getRate()方法 。 - Order:表示订单,有
orderNumber、orderDate、goodsList属性,以及相关的 Getter、Setter 方法和addList(Goods goods)方法 。 - Flight:描述航班,有
flightNumber等属性及对应方法,还有canCarry(double totalWeight)方法判断能否承载货物 。 - Calculate:用于计算,有构造方法和
getTotalWeight(Order order)、getTotalPrice(Order order, Pay pay, Customer customer)等计算方法 。
总结
-
类之间存在继承(如三角形箭头)和依赖(如虚线箭头及
<<sameFile>>标记表示在同一文件中相关联 )关系。继承关系体现了面向对象设计中的多态性,不同子类可根据自身特点实现父类的抽象方法;依赖关系表明类之间在功能实现上存在相互关联,如Calculate类在计算总价时依赖Order、Pay、Customer类 。 -
这些类可用于构建一个涉及客户管理、商品处理、订单操作、支付计算以及航班运输等功能的业务系统。
四、采坑心得
- 计算费率中的重量是指计费重量并非实际重量,导致结果有偏差。
- 关于抽象类,以及抽象方法,抽象类不可以构造,抽象方法,其子类必须要实现,多次因为没有实现而报错,建议最好用@override。
五.改进建议
- 输出时可以使用ToString来保证格式,实现输出的统一。
- 补充代码注释,梳理类方法结构提升内聚性。
六.总结
- 在设计类与类的关系时,首先一定要遵守职责单一原则,类与类之间如果要产生练习,需要中介类来完成计算
- 先构建好框架,把类先设计好,类与类之间的关系先明确,然后在去补全方法,具体实现需求,不要一开始条例不清楚,方法与属性随意放置,导致后续难以修改,同时做好代码的可扩展性,以便后续修改以及扩展。


浙公网安备 33010602011771号