一,前言

这次的航空货运管理系统与上次电梯调度有所不同,着重考察程序设计的各项原则以及继承,多态,接口,抽象类等知识点。在第一次设计好可拓展点,第二次增加新功能就很便捷迅速,非常考验对类的设计与包装。

两次题目概述如下:

通过这个概述,我们可以知道构建这个管理系统的几个核心业务:确定计费重量,计算基础运费,确定支付方式,处理客户信息,处理航班信息,以及处理订单信息。同时,要满足继承多态接口等可拓展要求,以及单一职责原则和开闭原则等多个设计原则,就必须合理设计类。

二,设计与分析

(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;
    }
}
5.航班类
点击查看代码
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;
    }
}
6.订单类
点击查看代码
//订单类
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.再多添加几组测试样例,这样说不定能检测出之前没发现的问题,改进程序。

五,总结

通过这两次的航空货运管理系统,我学会了运用继承多态以及接口和抽象类来实现一定的扩展性(如支持不同货物类型、支付方式和用户折扣),当然代码健壮性、可维护性和细节处理上仍有很大的进步空间,我对面向对象程序设计的几个重要原则有了更深刻的理解,也对程序测试更加熟练。同时,面对一些小的细节,比如在输入处理上我曾吃过很大的亏,现在也有了更多经验。