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中的“地位”,还是很高的,需要花费更多精力去学习。

再见
下次见!!!

posted @ 2025-05-25 10:44  等时圆~  阅读(39)  评论(0)    收藏  举报