第二次Blog作业

前言

这次Blog作业是第二次迭代大作业的总结,经过上次迭代大作业后我们面向对象编程的基础能力得到了很大提升,这次的大作业是一个航空货运管理系统的迭代,与上一次的电梯系统迭代相比,这次的航空货运系统没有了那么复杂的算法,所以我们少了实现算法的难度,但是这并不代表着这次大作业就简单了,因为这次大作业并没有给我们作为参考的类图设计,所以我们需要自己去思考类的设计,这就是本次大作业最大的难点,也是面向对象程序设计最需要的能力之一。在经历上次大作业的考验后,这次的迭代作业虽然也给了我很大的挑战,但是我并没有像上次那样手足无措,果然没有了算法的要求后压力小了不少,但是我依旧花了很长时间去完成作业,因为类图设计并没有那么简单,不比算法简单多少。从某种意义上说,这次迭代大作业更加适合我们培养面向对象程序设计的能力,因为在以后的工作中,算法可能并不是我们需要着重考虑的。下面是我对这次迭代大作业的概括:第一次作业就是基础的类设计,包括包括类、方法、控制结构、输入输出等知识;第二次则是增加了客户类型、货物类型、支付方式等类,使用了抽象类和接口,更强调面向对象设计与扩展性,在题量、输入复杂度与功能模块上均比第一次显著提升,体现了更强的系统建模能力。这篇Blog就是我对这次迭代大作业的总结和心得。

设计与分析

第一次航空货运管理系统

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

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

设计

下面是我设计的类图:

基本实现了题目要求的功能和设计。
下面是我的代码

点击查看代码
import java.util.*;

public class Main {
    static class Customer {
        String id, name, phone, address;
        public Customer(String id, String name, String phone, String address) {
            this.id = id; this.name = name; this.phone = phone; this.address = address;
        }
    }

    static class Cargo {
        String id, name;
        double width, length, height, weight;
        double chargeWeight;
        double rate;
        double cost;

        public Cargo(String id, String name, double width, double length, double height, double weight) {
            this.id = id; this.name = name;
            this.width = width; this.length = length; this.height = height;
            this.weight = weight;
            calcChargeWeight();
        }

        private void calcChargeWeight() {
            double volumeWeight = (width * length * height) / 6000.0;
            chargeWeight = Math.max(volumeWeight, weight);
        }

        public void calcRate() {
            if (chargeWeight < 20) rate = 35;
            else if (chargeWeight < 50) rate = 30;
            else if (chargeWeight < 100) rate = 25;
            else rate = 15;
            cost = chargeWeight * rate;
        }
    }

    static class Flight {
        String flightNo, from, to, date;
        double maxLoad;
        public Flight(String flightNo, String from, String to, String date, double maxLoad) {
            this.flightNo = flightNo; this.from = from; this.to = to; this.date = date; this.maxLoad = maxLoad;
        }
    }

    static class Order {
        String orderNo, orderDate, senderAddr, senderName, senderPhone, receiverAddr, receiverName, receiverPhone;
        Flight flight;
        List<Cargo> cargos;
        Customer customer;

        public Order(String orderNo, String orderDate, String senderAddr, String senderName, String senderPhone,
                     String receiverAddr, String receiverName, String receiverPhone, Flight flight, Customer customer) {
            this.orderNo = orderNo; this.orderDate = orderDate;
            this.senderAddr = senderAddr; this.senderName = senderName; this.senderPhone = senderPhone;
            this.receiverAddr = receiverAddr; this.receiverName = receiverName; this.receiverPhone = receiverPhone;
            this.flight = flight;
            this.customer = customer;
            cargos = new ArrayList<>();
        }

        public void addCargo(Cargo c) {
            cargos.add(c);
        }

        public double getTotalChargeWeight() {
            double total = 0;
            for (Cargo c : cargos) total += c.chargeWeight;
            return total;
        }

        public double getTotalCost() {
            double total = 0;
            for (Cargo c : cargos) total += c.cost;
            return total;
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // 读客户信息
        String custId = sc.nextLine().trim();
        String custName = sc.nextLine().trim();
        String custPhone = sc.nextLine().trim();
        String custAddr = sc.nextLine().trim();
        Customer customer = new Customer(custId, custName, custPhone, custAddr);

        int cargoCount = Integer.parseInt(sc.nextLine().trim());

        List<Cargo> cargoList = new ArrayList<>();
        for (int i = 0; i < cargoCount; i++) {
            String cargoId = sc.nextLine().trim();
            String cargoName = sc.nextLine().trim();
            double width = Double.parseDouble(sc.nextLine().trim());
            double length = Double.parseDouble(sc.nextLine().trim());
            double height = Double.parseDouble(sc.nextLine().trim());
            double weight = Double.parseDouble(sc.nextLine().trim());
            Cargo c = new Cargo(cargoId, cargoName, width, length, height, weight);
            c.calcRate();
            cargoList.add(c);
        }

        // 航班信息
        String flightNo = sc.nextLine().trim();
        String from = sc.nextLine().trim();
        String to = sc.nextLine().trim();
        String flightDate = sc.nextLine().trim();
        double maxLoad = Double.parseDouble(sc.nextLine().trim());
        Flight flight = new Flight(flightNo, from, to, flightDate, maxLoad);

        // 订单信息
        String orderNo = sc.nextLine().trim();
        String orderDate = sc.nextLine().trim();
        String senderAddr = sc.nextLine().trim();
        String senderName = sc.nextLine().trim();
        String senderPhone = sc.nextLine().trim();
        String receiverAddr = sc.nextLine().trim();
        String receiverName = sc.nextLine().trim();
        String receiverPhone = sc.nextLine().trim();

        Order order = new Order(orderNo, orderDate, senderAddr, senderName, senderPhone,
                receiverAddr, receiverName, receiverPhone, flight, customer);

        for (Cargo c : cargoList) {
            order.addCargo(c);
        }

        double totalWeight = order.getTotalChargeWeight();
        if (totalWeight > flight.maxLoad) {
            System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.\n", flight.flightNo);
            return;
        }

        double totalCost = order.getTotalCost();

        // 输出
        System.out.printf("客户:%s(%s)订单信息如下:\n", customer.name, customer.phone);
        System.out.println("-----------------------------------------");
        System.out.printf("航班号:%s\n", flight.flightNo);
        System.out.printf("订单号:%s\n", order.orderNo);
        System.out.printf("订单日期:%s\n", order.orderDate);
        System.out.printf("发件人姓名:%s\n", order.senderName);
        System.out.printf("发件人电话:%s\n", order.senderPhone);
        System.out.printf("发件人地址:%s\n", order.senderAddr);
        System.out.printf("收件人姓名:%s\n", order.receiverName);
        System.out.printf("收件人电话:%s\n", order.receiverPhone);
        System.out.printf("收件人地址:%s\n", order.receiverAddr);
        System.out.printf("订单总重量(kg):%.1f\n", totalWeight);
        System.out.printf("微信支付金额:%.1f\n", totalCost);
        System.out.println();
        System.out.println("货物明细如下:");
        System.out.println("-----------------------------------------");
        System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");

        int index = 1;
        for (Cargo c : order.cargos) {
            System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n",
                    index++, c.name, c.chargeWeight, c.rate, c.cost);
        }
    }
}

第一次作业侧重于实现一个完整的货运费用计算流程,要求我们掌握基本的类设计、重量判断与分段费率计算,以及控制台输入输出的规范处理。下面是我的SourceMonitor分析图:


文件结构还算合理,代码总行数为 163 行,包含 5 个类与接口,方法平均语句数为 10 条,表明方法控制在合适范围内,符合单一职责原则。最大圈复杂度为 5,出现在 Cargo.calcRate() 方法中,属于分段费率判断逻辑,复杂度可控。整体嵌套深度不高(最大为 3),平均复杂度仅为 1.67。

第二次航空货运系统

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

计算公式:基础运费 = 计费重量 × 费率 × 折扣率
其中,折扣率是指不同的用户类型针对每个订单的运费可以享受相应的折扣,
在本题中,用户分为个人用户和集团用户,其中个人用户可享受订单运费的9
折优惠,集团用户可享受订单运费的8折优惠。
三、题目说明
本次题目模拟某客户到该航空公司办理一次货运业务的过程:
航空公司提供如下信息:
航班信息(航班号,航班起飞机场,航班降落机场,航班日期,航班最大载
重量)
2
客户填写货运订单并进行支付,需要提供如下信息:
 客户信息(姓名,电话号码等)
 货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)
 运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选
航班号,订单日期)
 支付方式(支付宝支付、微信支付、现金支付)
注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独
计费。
程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信
息报表及货物明细报表。
四、题目要求
本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开
闭原则以及合成复用原则、依赖倒转原则,除需要在PTA平台提交源码外,还
需要在超星平台提交本次作业最终得分源码(首次提交最高分源码)的类图。

设计

这是我第二次迭代作业的类图:

第二次迭代在第一次的基础上,引入了客户类型、货物类型与支付策略等业务扩展,同时要求我们遵循面向对象设计原则,合理使用抽象类、枚举、接口和多态,构建具备良好可扩展性和复用性的系统结构,既检验了我们对 Java 语法和类结构的掌握,也锻炼了我们分析建模与系统设计的综合能力。
下面是我对相应功能的实现:


点击查看代码
    static class Flight {
        String flightNo, from, to, date;
        double maxLoad;

        public Flight(String flightNo, String from, String to, String date, double maxLoad) {
            this.flightNo = flightNo; this.from = from; this.to = to;
            this.date = date; this.maxLoad = maxLoad;
        }
    }

    interface PaymentStrategy {
        String getLabel();
    }

    static class WechatPay implements PaymentStrategy {
        public String getLabel() { return "微信"; }
    }

    static class AliPay implements PaymentStrategy {
        public String getLabel() { return "支付宝"; }
    }

    static class CashPay implements PaymentStrategy {
        public String getLabel() { return "现金"; }
    }

    static class Order {
        String id, date;
        String senderAddress, senderName, senderPhone;
        String receiverAddress, receiverName, receiverPhone;
        User user;
        List<Cargo> cargos = new ArrayList<>();
        Flight flight;
        PaymentStrategy payment;

        public Order(String id, String date, User user, Flight flight,
                     String senderAddress, String senderName, String senderPhone,
                     String receiverAddress, String receiverName, String receiverPhone,
                     PaymentStrategy payment) {
            this.id = id; this.date = date; this.user = user; this.flight = flight;
            this.senderAddress = senderAddress; this.senderName = senderName; this.senderPhone = senderPhone;
            this.receiverAddress = receiverAddress; this.receiverName = receiverName; this.receiverPhone = receiverPhone;
            this.payment = payment;
        }

        public void addCargo(Cargo c) {
            cargos.add(c);
        }

        public double getTotalWeight() {
            return cargos.stream().mapToDouble(c -> c.chargeWeight).sum();
        }

        public double getTotalCost() {
            double sum = cargos.stream().mapToDouble(c -> c.cost).sum();
            return sum * user.getDiscountRate();
        }

        public void printSummary() {
            double totalWeight = getTotalWeight();
            if (totalWeight > flight.maxLoad) {
                System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.\n", flight.flightNo);
                return;
            }

            System.out.printf("客户:%s(%s)订单信息如下:\n", user.name, user.phone);
            System.out.println("-----------------------------------------");
            System.out.printf("航班号:%s\n", flight.flightNo);
            System.out.printf("订单号:%s\n", id);
            System.out.printf("订单日期:%s\n", date);
            System.out.printf("发件人姓名:%s\n", senderName);
            System.out.printf("发件人电话:%s\n", senderPhone);
            System.out.printf("发件人地址:%s\n", senderAddress);
            System.out.printf("收件人姓名:%s\n", receiverName);
            System.out.printf("收件人电话:%s\n", receiverPhone);
            System.out.printf("收件人地址:%s\n", receiverAddress);
            System.out.printf("订单总重量(kg):%.1f\n", totalWeight);
            System.out.printf("%s支付金额:%.1f\n", payment.getLabel(), getTotalCost());

            System.out.println("\n货物明细如下:");
            System.out.println("-----------------------------------------");
            System.out.println("明细编号    货物名称    计费重量    计费费率    应交运费");
            int idx = 1;
            for (Cargo c : cargos) {
                System.out.printf("%d    %s    %.1f    %.1f    %.1f\n",
                        idx++, c.name, c.chargeWeight, c.rate, c.cost);
            }
        }
    }
###分析 第二次迭代作业相比第一次显著提升了复杂度与设计要求,我感受到本次任务不再只是简单实现功能逻辑,而是更加强调面向对象设计思维和系统建模能力的培养。 这次作业新增了“客户类型”“货物类型”“支付方式”等多种业务因素,要求我们使用枚举、抽象类、接口、策略模式等面向对象技术进行建模,这让我不仅复习了类与继承,还真正理解了多态和解耦的实际意义。 例如,使用 PaymentStrategy 接口实现微信、支付宝、现金的多样支付方式,就是对策略模式的直接应用,让我学会了如何应对“系统行为变化”的设计问题。 以下是我的SourceMonitor分析图:

踩坑心得

在这两次迭代作业中我也踩了很多的坑,犯了很多的错误,修改了无数遍代码,以下是我两次迭代作业中分别踩的坑以及踩坑心得:

第一次作业

1.体积重量计算公式易忽略单位

我直接使用长×宽×高作为体积重量,忘记了需除以6000(cm³转kg),导致计费重量错误。

2.计费重量与实际重量选取搞混

应该取二者中的较大值,部分实现误用了较小值,影响最终运费计算。

3.分段费率实现混乱

if-else 结构写得不清晰,边界条件(如 weight == 20)判断错误,导致分段费率不准确。

4.输出格式对不齐

明细表中的列数对不整齐,尤其是在多位数字或中文宽度处理上,没有使用格式化输出。

心得

在第一次航空货运管理系统的开发过程中,我遇到了不少初级但关键的陷阱。最常见的是体积重量的计算单位被忽略,没有除以 6000,导致计费重量错误。此外,对于“计费重量应取实际重量和体积重量的较大值”的逻辑,初始实现时一度理解错方向,使用了较小值。分段费率的 if-else 写法也让我调试了许久,尤其是判断边界值(如恰好 20kg、50kg)时容易出错。输出方面,我在格式控制上也踩了坑,明细表对齐混乱,实型数未保留一位小数,影响了测试结果。虽然功能整体不复杂,但这次作业让我意识到逻辑准确性与输出规范性在编程中的重要性,也为后续的结构化设计打下了基础。

第二次作业

1.客户类型判断不一致

"Corporate" 和 "Group" 混用,导致对象创建错误。

2.支付方式错误

仍用 if-else 控制支付输出,没有将其抽象为接口,丧失了设计层面的灵活性。

3.订单运费未考虑折扣

虽然货物计算正确,但总费用未乘以客户类型对应的折扣率,导致结果偏高。

4.构造函数参数过长、耦合重

类之间参数传递链条太长,没有使用合理的封装方法,构造器过于复杂。

5.注释严重不足

尽管结构更清晰,但由于类和方法增多,未配套注释,所以代码难以理解和维护。

6.输出中未保留一位小数

有些金额或重量直接输出了多位小数,没有使用 %.1f 控制格式,无法通过测试样例。

心得

第二次迭代作业在功能和结构上全面升级,也带来了更多挑战。我最先遇到的问题是枚举类型的使用不熟练,在将输入字符串转换为 CargoType 时因大小写不匹配报错,调试时间较长。客户类型的判断逻辑也容易混淆 "Individual" 和 "Corporate",缺乏统一转换处理。在设计上,我一开始未使用策略接口处理支付方式,而是沿用传统的 if-else,导致后续难以扩展。更严重的是,我忽略了订单总费用需乘以客户折扣率,导致输出金额偏高。此外,由于这次类数量较多,构造器传参过多、注释不足也造成阅读困难。这次踩坑经历让我深刻体会到:设计模式、抽象结构和面向对象的清晰分工不仅提升可扩展性,也能显著减少重复调试和逻辑遗漏的问题。

改进建议

1.在第一次作业中,我将多个功能集中在主类 Main 中处理,导致类的职责不清晰、方法冗长。第二次虽然引入了抽象类和接口,但部分构造函数参数仍然过多,存在类之间耦合过紧的问题。经过网上寻求解决方法了解到我可以通过引入数据模型类来简化类间依赖关系。
2.在计算运费、判断货物类型、支付方式等逻辑中,我有重复使用的判断语句和流程,未能充分抽象成方法。后续可以将这类逻辑封装为工具类,提升代码复用性和可维护性。
3.尽管满足了保留 1 位小数的要求,但我在多个地方使用了 printf 和 String.format() 混合输出,存在不统一的问题。我后续可以定义一个统一的格式化输出方法,保持报表输出风格一致,提高输出的可读性。
4.虽然我能较为清晰地编写逻辑,但整体代码注释不足。未来应在每个类、复杂方法、关键逻辑前加入简要说明,方便后续调试和代码复查,也有助于代码被他人理解和复用。

总结

本次“航空货运管理系统”作业通过两次迭代,使我完整经历了从功能实现到系统设计的过程。第一次作业,我专注于程序逻辑的正确性与输入输出的规范处理,掌握了体积重量计算、分段费率判断和基本类结构的搭建。第二阶段则引入了更多未来在实际工作中要考虑的业务因素,如客户类型、货物类型、支付方式等,使我不得不从面向对象的角度重新思考系统的结构与扩展。
在迭代过程中,我逐步理解了抽象类、接口、枚举等知识在解决实际问题中的作用,也意识到良好的设计不仅能满足当前需求,更能为未来的功能拓展提供坚实基础。同时,通过调试与踩坑,我对代码规范、输入健壮性、输出格式化和职责划分有了更深刻的认知。

最后我想说的是,这次作业不仅锻炼了我的编程能力,更让我初步建立了面向对象设计的思维方式,为后续开发更复杂、可维护的项目奠定了坚实的基础。

posted @ 2025-05-24 18:51  tyc114514  阅读(38)  评论(1)    收藏  举报