NCHU OOP BLOG2--航空货运管理系统
NCHU OOP BLOG2--航空货运管理系统
目录
1.前言
2.设计与分析
3.踩坑心得
4.改进建议
5.总结
正文
1.前言
这两次的作业经历五一假期,周期拉的很长,但题量不多,难度也不大,因为重点不在算法上面,主要是加强我们对继承、抽象类的理解。
这两道题难点主要在让我们自己进行类的拆分,设计类便于进行继承,符合七大设计原则。还有就是题目的信息量很大,要对每一个信息进行解释,然后再输出,比较繁琐。
整体上来说,还好,比第一次大作业难度小了很多,花费的时间也更少了点,就是第一次作业在五一期间,要再五一前写完,还是挺累的。
2.设计与分析
话不多说,直接进入正题!
第一次大作业
PS:在第一次货运系统之前,用其他题目的重构给我们练了练手。
7-1 NCHU_点线面问题重构(继承与多态)
点面之前在类设计的时候写过了,这次重构难度不大,还给了类图,主要是在点面上抽象出父类Element类,用于实现多态和统一的管理,也方便了输出点面的信息。
比较简单,不细讲了。

7-2 NCHU_雨刷程序功能扩展设计
这道题就是我们的老伙伴了,雨刷问题,第三次见面了。
这次,又对它进行了重构,多加了一种类型的雨刷,两种雨刷并存,因此要对雨刷、控制类、操作杆、刻度盘都要进行抽象,再继承,分离出两套系统来。
这道题目还是有点难度的,比较繁琐,需要细心去写。

小结:这两道题难度还是有的,有浅入深的来帮助我们进行继承的训练,都是以前的题目,也不会觉得十分陌生。
嘻嘻,终于进入正题了!
7-3 NCHU_航空货运管理系统(类设计)

某航空公司“航空货运管理系统”中的空运费的计算涉及多个因素,通常包括货物重量/体积、运输距离、附加费用、货物类型、客户类型以及市场供需等。
本次作业主要考虑货物重量/体积,以下是具体的计算方式和关键要点:
一、计费重量的确定
空运以实际重量(Gross Weight)和体积重量(Volume Weight)中的较高者作为计费重量。
计算公式:
体积重量(kg) = 货物体积(长×宽×高,单位:厘米)÷ 6000
示例:
若货物实际重量为 80kg,体积为 120cm×80cm×60cm,则:
体积重量 = (120×80×60) ÷ 6000 = 96kg
计费重量取 96kg(因 96kg > 80kg)。
二、基础运费计算
费率(Rate):航空公司或货代根据航线、货物类型、市场行情等制定(如CNY 30/kg)。本次作业费率采用分段计算方式:

公式:基础运费 = 计费重量 × 费率
三、题目说明
本次题目模拟某客户到该航空公司办理一次货运业务的过程:
航空公司提供如下信息:
航班信息(航班号,航班起飞机场所在城市,航班降落机场所在城市,航班日期,航班最大载重量)
客户填写货运订单并进行支付,需要提供如下信息:
客户信息(姓名,电话号码等)
货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)
运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选航班号,订单日期)
支付方式(支付宝支付、微信支付)
注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独计费。
程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信息报表及货物明细报表。
四、题目要求
本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开闭原则以及合成复用原则,除需要在 PTA 平台提交源码外,还需要在超星平台提交本次作业最终得分源码(首次提交最高分源码)的类图,评判标准为:
基础得分:PTA 实际得分
设计因素:单一职责原则(40%)、里氏代换原则(20%)、开闭原则(20%)、合成复用原则(20%)
输入输出格式


输入格式:
按如下顺序分别输入客户信息、货物信息、航班信息以及订单信息。
客户编号
客户姓名
客户电话
客户地址
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为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位小数。
题目及代码分析
分析题目的需求,由标黑部分可知,至少设计9个类,包括:客户类、航班类、货物类、订单类、订单信息类、收件人、发件人、支付类、控制类;
其中,(1)顾客类、收件人、发件人有相同信息,衍生出人这个类,合成复用;


(2)支付类,为抽象类,衍生出,微信支付,支付宝支付两个子类;
(3)货物类,包括货物属性:长宽高,重量,ID,名字;
(4)订单类,包括货物,以及货物的数量;
(5)订单明细类,包括订单、该订单的时间,订单收件人、发件人;

(5)控制类,用于联系顾客类和订单明细类,避免两个类的直接联系,实现顾客类和订单明细类的解耦;其中,含有订单明细类的数组;

再来写,各个类中的方法:
(1)货物类:
1.基本的构造方法,get、set方法;
2.计算该货物的计费重量;

3.计算该货物的运费;

(2)货物订单类:
1.基本的构造方法,get、set方法;
2.得到该订单总金额;

3.得到该订单的总计费重量;

(3)支付类:
1.基本的构造方法,get、set方法;
2.抽象方法:支付;
(4)其他类:基本的构造方法,get、set方法。
最后,主函数中:
一步步分解,题目信息,创建对象,将信息储存在相应类里,最后打印订单信息,比较繁琐;
String customerID = input.nextLine();
String Name = input.nextLine();
String PhoneNumber = input.nextLine();
String Adress = input.nextLine();
Human human = new Human(Name,Adress,PhoneNumber);
Customer customer = new Customer(customerID,human);
LinkedList<Cargo> list = new LinkedList<>();
int N = input.nextInt();
for(int i = 0; i<N;i++) {
int CargoID = input.nextInt();
input.nextLine();
String name = input.nextLine();
double width = input.nextDouble();
double length = input.nextDouble();
double height = input.nextDouble();
double weight = input.nextDouble();
Cargo cargo = new Cargo(CargoID,name,width,length,height,weight);
list.add(cargo);
}
input.nextLine();
String flightNumber = input.nextLine();
String departureAirport = input.nextLine();
String arrivalAirport = input.nextLine();
String planeDate = input.nextLine();
LocalDate date = LocalDate.parse(planeDate);
double maxWeight = input.nextDouble();
PlaneDetail plane = new PlaneDetail(flightNumber,departureAirport,arrivalAirport,date,maxWeight);
input.nextLine();
String CargoOrderID = input.nextLine();
String orderDate = input.nextLine();
LocalDate OrderDate = LocalDate.parse(orderDate);
CargoOrder cargoOrder = new CargoOrder(CargoOrderID,list);
Adress = input.nextLine();
Name = input.nextLine();
PhoneNumber = input.nextLine();
human = new Human(Name,Adress,PhoneNumber);//PhoneNumber
Sender sender = new Sender(human);
Adress = input.nextLine();
Name = input.nextLine();
PhoneNumber = input.nextLine();
human = new Human(Name,Adress,PhoneNumber);
Recipient recipient = new Recipient(human);
OrderDetail orderDetail = new OrderDetail(cargoOrder,sender,OrderDate,recipient);
LinkedList<OrderDetail> orderDetailList = new LinkedList<>();
orderDetailList.add(orderDetail);
Controller controlelr = new Controller(orderDetailList);
customer.setController(controlelr);
double sumWeight = 0;
for(int i = 0 ; i< customer.getController().getOrderDetailList().getFirst().getCargoorder().getList().size(); i++) {
sumWeight = sumWeight + customer.getController().getOrderDetailList().getFirst().getCargoorder().getList().get(i).getPriceWeight();
}
if(sumWeight>plane.getMaxWeight()) {
System.out.println("The flight with flight number:" + plane.getFlightNumber() + " has exceeded its load capacity and cannot carry the order.");
return ;
}
System.out.println("客户:"+ customer.getCustomer().getName() +
"(" + customer.getCustomer().getPhoneNumber() + ")订单信息如下:" );
System.out.println("-----------------------------------------");
System.out.println("航班号:" + plane.getFlightNumber());
System.out.println("订单号:" + customer.getController().getOrderDetailList().getFirst().getCargoorder().getCargoOrderID());
System.out.println("订单日期:" + customer.getController().getOrderDetailList().getFirst().getDate());
System.out.println("发件人姓名:" + customer.getController().getOrderDetailList().getFirst().getSender().getSender().getName());
System.out.println("发件人电话:" + customer.getController().getOrderDetailList().getFirst().getSender().getSender().getPhoneNumber());
System.out.println("发件人地址:" + customer.getController().getOrderDetailList().getFirst().getSender().getSender().getAdress());
System.out.println("收件人姓名:" + customer.getController().getOrderDetailList().getFirst().getRecipient().getRecipient().getName());
System.out.println("收件人电话:" + customer.getController().getOrderDetailList().getFirst().getRecipient().getRecipient().getPhoneNumber());
System.out.println("收件人地址:" + customer.getController().getOrderDetailList().getFirst().getRecipient().getRecipient().getAdress());
System.out.printf("订单总重量(kg):%.1f\n",sumWeight);
System.out.printf("微信支付金额:%.1f\n",customer.getController().getOrderDetailList().getFirst().getCargoorder().getsum());
System.out.println("");
System.out.println("货物明细如下:");
System.out.println("-----------------------------------------");
System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
for(int i = 1; i<= customer.getController().getOrderDetailList().getFirst().getCargoorder().getList().size(); i++) {
System.out.printf("%d\t",i);
//1 发电机 80.0 25.0 2000.0
System.out.printf("%s\t",customer.getController().getOrderDetailList().getFirst().getCargoorder().getList().get(i-1).getName());
double priceWeight = customer.getController().getOrderDetailList().getFirst().getCargoorder().getList().get(i-1).getPriceWeight();
System.out.printf("%.1f\t", priceWeight);
double rate;
if(priceWeight < 20) {
rate = 35;
}
else if(priceWeight >= 20 && priceWeight < 50) {
rate = 30;
}
else if(priceWeight >= 50 && priceWeight < 100) {
rate = 25;
}
else {
rate = 15;
}
System.out.printf("%.1f\t", rate);
System.out.printf("%.1f\n",customer.getController().getOrderDetailList().getFirst().getCargoorder().getList().get(i-1).getPrice());
}
代码分析完了,让我们来看下类图,和代码的圈复杂度;

由类图可知,
1.WeChat、Alipay继承了Payment类,而Payment类与OrderDetail类为依赖关系;作为方法的参数,执行OrderDetail类中支付这一方法;
2.Customer、Sender、Recipient类均与Human类成关联关系,为合成复用;
3.Customer与controller呈关联关系,Controller与OrderDetail为聚合关系。
4.OrderDetial与Sender和Recipient成关联关系;
5.OrderDetail与CargoOrder呈现关联关系,而CargoOrder与Cargo呈聚合关系;
基本和刚刚分析的一致;


从圈复杂度可以看出,大部分的数据没有在绿色范围内,代码质量不高,最大复杂度为11,平均复杂度为1.24,相差很大,注释数量也很少。
在主函数中,拆分信息用了较多的if语句,使得复杂度增加;
下次,可以增加代码注释的数量,用更合理的方法拆分信息;
以上,就是第一次大作业的全部内容
第一次大作业分析结束!!!
第二次大作业
PS:老规矩,先分析一下前两道小题目;
7-1 NCHU_魔方问题
这道题算是新题了,以前没有做过,这道提的考点主要还是在继承和多态上,还是有点难度的;感觉下次应该会在这道题的基础上再次同构。给了类图,大大降低了难度。

主要是在怎么计算每个魔方的面积和体积上,数学问题,以及如何联系RubikCube和Soild两个类中的得到面积和得到体积的方法,;明白了之后,问题就迎刃而解了。

7-2 NCHU_点线面问题再重构(容器类)
又见到老朋友了,点面问题,这次在上次的基础上又增加了添加GeometryObject容器类的要求,将点面线的对象储存到容器类中,统一管理,方便查询删除某个对象,统一输出。

感觉这次容器类的添加还是很有必要的,但是,主函数中分支数量会大幅度增加,对对象的操作不同了。

啦啦啦,来到了我们的重头戏
7-3 NCHU_航空货运管理系统(继承与多态)




上图为本题相较于第一次需要重构的地方,需拓展的类增加为了3个;
因此,输入格式也有了点变化,输出格式没变;
输入格式
客户类型[可输入项:Individual/Corporate]
客户编号
客户姓名
客户电话
客户地址
货物类型[可输入项:Normal/Expedite/Dangerous]
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为YYYY-MM-DD)
航班最大载重量
订单编号
订单日期(格式为YYYY-MM-DD)
发件人地址
发件人姓名
发件人电话
收件人地址
收件人姓名
收件人电话
支付方式[可输入项:Wechat/ALiPay/Cash]
题目及代码分析
由上变化进行在本次题目的需求分析,
1.货物类,修改为抽象类,含有抽象方法:计算货物收费金额;
abstract class Cargo {
private int CargoID;
private String name;
private double weight;
private double height;
private double width;
private double length;
private Version version;
public Cargo() {
super();
}
public Cargo(int cargoID, String name, double width, double length, double height, double weight,Version version) {
super();
CargoID = cargoID;
this.name = name;
this.weight = weight;
this.height = height;
this.width = width;
this.length = length;
this.version = version;
}
public int getCargoID() {
return CargoID;
}
public void setCargoID(int cargoID) {
CargoID = cargoID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public Version getVersion() {
return version;
}
public void setVersion(Version version) {
this.version = version;
}
public double getPriceWeight() {
double VWeight;
double PriceWeight;
VWeight = this.length * this.height * this.width / 6000;
if(VWeight>this.weight) {
PriceWeight = VWeight;
}
else PriceWeight = this.weight;
return PriceWeight;
}
public abstract double getPrice() ;}
2.衍生出三个类:加急货物类、危险货物类、正常类,每个类的费率不同;覆写,计算运费方法;面向Cargo这个父类(抽象类),实现运行时多态;

3.顾客类,修改为抽象类,含有抽象方法,得到折扣;


4.衍生出个人用户和公司用户,覆写得到折扣方法;

5.增加现金支付类,继承Payment类;覆写支付这一抽象方法,输出“现金支付:+金额”;

6.其他基本不变,只修改一些构造方法,增加get、set方法;
最后,在主函数中,判断货物、顾客、支付类型,进行简单修改;


代码分析部分就结束了,来看看类图和圈复杂度吧!

由上面类图,可以看到类与类之间的关系还是挺复杂的,在继承关系上,增加了一些子类,大体上没有变化(与第一次相比);增加了两个enum类,用于记录相应的类型;
由图,关联关系都是与父类(抽象类)相联系,不面向细节编程,而是面向抽象编程;

由圈复杂度可以看出,算法方面没有什么难度,深度最大为3,平均复杂度为1.33,但是最大复杂度还是远远超过绿色范围,主要问题还是出在主函数上,每次判断类型都是if_else语句,分支数量增多,最大复杂度增加。
注释的比例还是很低,不写注释是一个很不好的习惯,需要在日后学习中时刻注意,有意识增加注释的比例,随后养成习惯,下意识写注释。
以上,就是第二次大作业的全部内容
让我们恭喜,这Part圆满结束!!!
3.踩坑心得
这几次的大作业,踩的坑就第一阶段的坑多了,主要是这一次作业难度更低,而且时刻注意着,不要踩坑里。还有就是预留了足够的时间,即使踩了坑,也有足够的时间去修改;

1.第一,还是软件的一生公敌——需求;需求上出错,就要花更多的时间来修改。前几次提交,都是多种错误,包括格式错误和答案错误;尤其是答案错误,因为分析时,以为得到货物总重量,是得到货物的实际重量,但其实是得到计费总重量;

2.第二,其实就是格式问题啦,这是大部分人普遍犯的错误,大多数时候“答案错误”,其实就是格式错误,但还是会傻傻的以为是自己哪里逻辑有问题,一遍遍的查看修改,最后发现是格式错误,是真的会爆炸的;

还有一个点,可能不算坑,个人的习惯问题,但是我还是十分想说,就是我大部分时候出错,是复制粘贴有部分相同的代码后,没有及时修改,导致有几句一样,或者出错,没有发现,最后出错;真的很人机啊,以后写代码,要时刻注意,要减少这样的问题出现。
这部分这样了,踩坑不多!
4.改进建议
关于代码的改进,前面也有简单谈到,这来总结一下;
(1)增加更多的注释,两次的注释率都很低,提高代码的可读性;
(2)增加处理一些异常的代码,比如输入有错误等异常情况;
(3)修改一些函数,处理一下if——else的情况,可以用简单工厂模式,修改主函数中,创建不同子类部分的代码,更切合实际软件的设计;
浅浅结束这一Part吧!
5.总结
第二阶段的练习,不知不觉就结束了,这一阶段相较于第一阶段,难度降低了很多,至此,来简单总结下:
(1)这一阶段,通过对继承和抽象类的练习,能够运行用继承的相关知识,能在实际问题中,更合理设计类;
(2)同时,对七大设计原则,有了更深刻的理解,将它们运用到实际的类设计中。
(3)当然,还存在一些问题,对于合成复用和继承,还是不能很好的区分,什么时候是继承,什么时候用合成复用,还需要再后面的学习中,更一步的学习和了解。
(4)下一阶段,我将更侧重于接口的学习和运用,俗话说,java是面向接口编程,接口在java中的“地位”,还是很高的,需要花费更多精力去学习。
再见
下次见!!!

浙公网安备 33010602011771号