一,前言
这次的航空货运管理系统与上次电梯调度有所不同,着重考察程序设计的各项原则以及继承,多态,接口,抽象类等知识点。在第一次设计好可拓展点,第二次增加新功能就很便捷迅速,非常考验对类的设计与包装。
通过这个概述,我们可以知道构建这个管理系统的几个核心业务:确定计费重量,计算基础运费,确定支付方式,处理客户信息,处理航班信息,以及处理订单信息。同时,要满足继承多态接口等可拓展要求,以及单一职责原则和开闭原则等多个设计原则,就必须合理设计类。
二,设计与分析
(1)第一次航空货运管理系统
程序设计

1.这里我们给确定计费重量,计算基础运费以及确定支付方式这个三个功能设计成接口,因为他没有属性成员,只是一个可以抽象的行为,没必要用抽象类,用接口声明方法然后在具体的类里再去具体实现。
2.给货物类设计成抽象类,因为货物有多个属性,同时让他实现确定计费重量的接口,这里就要重写该接口中的抽象方法。同时由于不知道后面的迭代对货物类型有没有拓展,这里再让一个具体类继承该抽象货物类,里面只需用到super。
3.同理,我们也要给计算运费的接口找一个能实现它的具体类,因为该类主要描述一种计算行为,所以直接设计一个具体类实现确定基础运费的接口,并重写该接口里的抽象方法。
4.这里只涉及到了微信支付,那就设计一个微信支付类实现支付方式的接口并重写接口抽象方法。
5.设计一个客户类,它主要保存客户的ID,姓名,电话,住址信息
6.设计一个航班类,它主要保存该航班的航班号,起飞/落地机场,飞行日期以及最大载客量。
7.设计一个订单类,它主要保存该次订单的ID,日期,寄件人姓名,电话,地址,以及收件人姓名,电话和地址。
(同时上面三个类还要有一些其他的相应方法)
9.由于要满足单一职责原则,且输入量和输出量都非常大,要是全部放在主类里会很杂乱,这里单独把输入输出作为两类
10.程序主入口类,非常简短,只需要创建对象然后调用方法即可。
程序分析
1.货物类以及计费重量
点击查看代码
//1-货物相关接口,定义获取计费重量等方法
interface TheGoods {
double getFinalWeight();
}
//1.1-抽象货物类-&实现货物接口
abstract class AbstractGoods implements TheGoods {
protected int goodsId;
protected String name;
protected double width;
protected double length;
protected double height;
protected double weight;
public AbstractGoods(int goodsId, String name, double width, double length, double height, double weight) {
this.goodsId = goodsId;
this.name = name;
this.width = width;
this.length = length;
this.height = height;
this.weight = weight;
}
public String getName() { return name; }
//计算体积重量
protected double calculateVolumeWeight() {
return (width * length * height) / 6000;
}
//重写接口里的抽象方法
public double getFinalWeight() {
double volumeWeight = calculateVolumeWeight();
return Math.max(weight, volumeWeight);
}
}
//1.2-具体货物类,继承抽象货物类
class Goods extends AbstractGoods {
public Goods(int goodsId, String name, double width, double length, double height, double weight) {
super(goodsId, name, width, length, height, weight);
}
}
2.计算基础运费
点击查看代码
//2-运费计算接口
interface FeeCalculator {
double calculateFee(double chargeableWeight);
}
//2.1-实际运费计算类-&实现运费计算接口
class ActualFeeCalculator implements FeeCalculator {
//重写接口里的抽象方法
public double calculateFee(double chargeableWeight) {
if (chargeableWeight < 20) {
return chargeableWeight * 35;
} else if (chargeableWeight < 50) {<details>
return chargeableWeight * 30;
} else if (chargeableWeight < 100) {
return chargeableWeight * 25;
} else {
return chargeableWeight * 15;
}
}
}
3.支付方式
点击查看代码
//3-支付方式接口
interface PaymentMethod {
void pay(double amount);
}
//3.1-微信支付类-&实现支付方式接口
class WechatPayment implements PaymentMethod {
//重写接口里的抽象方法
public void pay(double amount) {
System.out.println("微信支付金额: " + amount);
}
}
4.客户类
点击查看代码
//客户类
class Customer {
private int customerId;
private String name;
private String phone;
private String address;
public Customer(int customerId, String name, String phone, String address) {
this.customerId = customerId;
this.name = name;
this.phone = phone;
this.address = address;
}
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
}
点击查看代码
class Flight {
private String flightNumber;
private String departureAirport;
private String arrivalAirport;
private String date;
private double maxLoadCapacity;
public Flight(String flightNumber, String departureAirport, String arrivalAirport, String date, double maxLoadCapacity) {
this.flightNumber = flightNumber;
this.departureAirport = departureAirport;
this.arrivalAirport = arrivalAirport;
this.date = date;
this.maxLoadCapacity = maxLoadCapacity;
}
public String getFlightNumber() {
return flightNumber;
}
// 判断是否超载
public boolean isOverload(double totalWeight) {
return totalWeight > maxLoadCapacity;
}
}
点击查看代码
//订单类
class Order {
private int orderId;
private String orderDate;
private String senderAddress;
private String senderName;
private String senderPhone;
private String recipientAddress;
private String recipientName;
private String recipientPhone;
private List<Goods> goodsList = new ArrayList<>();//这里要用List
private FeeCalculator feeCalculator;
//备用
//private PaymentMethod paymentMethod;
public Order(int orderId, String orderDate, String senderAddress, String senderName, String senderPhone,
String recipientAddress, String recipientName, String recipientPhone, FeeCalculator feeCalculator) {
this.orderId = orderId;
this.orderDate = orderDate;
this.senderAddress = senderAddress;
this.senderName = senderName;
this.senderPhone = senderPhone;
this.recipientAddress = recipientAddress;
this.recipientName = recipientName;
this.recipientPhone = recipientPhone;
this.feeCalculator = feeCalculator;
}
public int getOrderId() {
return orderId;
}
public String getOrderDate() {
return orderDate;
}
public String getSenderAddress() {
return senderAddress;
}
public String getSenderName() {
return senderName;
}
public String getSenderPhone() {
return senderPhone;
}
public String getRecipientAddress() {
return recipientAddress;
}
public String getRecipientName() {
return recipientName;
}
public String getRecipientPhone() {
return recipientPhone;
}
public List<Goods> getGoodsList() {
return goodsList;
}
public FeeCalculator getFeeCalculator() {
return feeCalculator;
}
// 添加货物到订单
public void addGoods(Goods goods) {
goodsList.add(goods);
}
// 计算订单总重量
public double calculateTotalWeight() {
double totalWeight = 0;
for (int i = 0; i < goodsList.size(); i++) {
totalWeight += goodsList.get(i).getFinalWeight();
}
return totalWeight;
}
// 计算订单总费用
public double calculateTotalFee() {
double totalFee = 0;
for (int i = 0; i < goodsList.size(); i++) {
totalFee += feeCalculator.calculateFee(goodsList.get(i).getFinalWeight());//goodsList.get(i)用来获取当前索引位置的元素
}
return totalFee;
}
}
p.s.剩下的输入类和输出类以及主类比较简单,主要就是一些格式化,主函数非常简单,就不再多加展示。

(2)第二次航空货运管理系统
第二次的管理系统只需要在第一次的基础上稍作增加和修改即可,这就是拓展的好处。
1.程序设计

1.整体就是在第一次的基础上稍加修改
2.对支付方式进行拓展,对客户类型进行拓展(不同类型的客户有不同的折扣)
2.程序分析
1.首先我们对支付方式进行拓展,只需要再建立两个类来实现支付方式的接口即可,也就是支付宝支付和现金支付的类,直接重写pay方法即可
点击查看代码
//3.2-支付宝支付类-实现支付方式接口
class AlipayPayment implements PaymentMethod {
@Override
public void pay(double amount) {
System.out.println("支付宝支付金额:" + amount);
}
}
//3.3-现金支付类-实现支付方式接口
class CashPayment implements PaymentMethod {
@Override
public void pay(double amount) {
System.out.println("现金支付金额:" + amount);
}
}
2.对客户类型进行拓展,分别让个人客户和团体客户继承客户类,分别返回不同的客户名称和折扣
点击查看代码
// 个人用户
class IndividualCustomer extends Customer {
public IndividualCustomer(int customerId, String name, String phone, String address) {
super(customerId, name, phone, address);
}
@Override
public String getUserType() {
return "Individual";
}
@Override
public double getDiscountRate() {
return 0.9; //9折
}
}
//团体用户
class CorporateCustomer extends Customer {
public CorporateCustomer(int customerId, String name, String phone, String address) {
super(customerId, name, phone, address);
}
@Override
public String getUserType() {
return "Corporate";
}
@Override
public double getDiscountRate() {
return 0.8; //8折
}
}
3.同时要对输入类Input经行一些修改确保输入格式加入了客户类型,保证输入正确。并把上一次系统里输出类Output里的内容改进,在FeeCalculator接口里保留原有的calculateFee方法,并添加calculateRate方法计算费率及确保输出类的单一职责,只做输出。这里不做展示。

三,踩坑心得
1.接口方法实现遗漏
在实现FeeCalculator接口时,calculateRate方法最初未写返回语句,导致编译报错。需注意接口中所有抽象方法必须完整实现,包括返回值和参数类型的严格匹配。
2.支付方式初始化问题
Order类中paymentMethod成员变量未正确初始化,在Input.getOrderInfo方法中,需根据用户输入的支付方式字符串(如 "Wechat")实例化对应的支付类(如WechatPayment),否则会导致空指针异常。
3.输入处理时的缓冲区残留
使用Scanner读取输入时,nextInt()等方法不会读取换行符,导致后续nextLine()可能读取到残留的换行符。需在每次读取数值后调用scanner.nextLine()清空缓冲区,例如在读取goodsCount后添加scanner.nextLine(),避免干扰后续字符串输入。
4.订单与货物的关联缺失
在main方法中,创建Order对象后未将goodsList添加到订单中,需通过order.addGoods()方法手动关联,否则订单的货物列表为空,计算总重量和费用时会出错。
四,改进建议
1.可以完善异常处理,用最近新学的try-catch语句尝试捕获异常。
2.提高程序的抽象性和代码的复用性,除了这两次迭代,这个系统还可以继续更新,这时原来没有拓展性的类在新加功能时,处理起来就会很复杂,比如Fight类和Order类。
3.再多添加几组测试样例,这样说不定能检测出之前没发现的问题,改进程序。
五,总结
通过这两次的航空货运管理系统,我学会了运用继承多态以及接口和抽象类来实现一定的扩展性(如支持不同货物类型、支付方式和用户折扣),当然代码健壮性、可维护性和细节处理上仍有很大的进步空间,我对面向对象程序设计的几个重要原则有了更深刻的理解,也对程序测试更加熟练。同时,面对一些小的细节,比如在输入处理上我曾吃过很大的亏,现在也有了更多经验。
浙公网安备 33010602011771号