第二次Blog作业

前言

相比于第一次的电梯程序设计,我认为这一次的迭代作业并没有太多的算法要求,在逻辑等方面上更加清晰可观,没有冗杂和难解的算法逻辑问题,做起来并没有很困难,但此次作业逐步加深了对面向对象设计的理解,从简单的继承多态到复杂系统的类协作,每一次迭代都在优化代码结构和逻辑。但通过这三次作业,深入实践了面向对象设计中的继承、多态、容器类应用及复杂业务逻辑处理,进一步理解了单一职责、开闭原则、合成复用原则等设计思想的实际运用。此次大作业较第一次大作业总共分为2次作业,较为简单,基本没算法,但代码量有所增加,第一次作业100分,第二次作业100分,完成情况较好,基本没失分的地方,但依旧有些方法使用不熟悉,印象不深刻,例如:工程模式,抽象等方法使用较少,还比较生疏需要加强练习。

.知识点:

这两次大作业整体难度不大,主要考核点是类设计以及对开闭原则,单一职责原则、里氏代换原则,合成复用原则、依赖倒转原则的运用还有对继承和多态,抽象类接口的理解以及运用。

  1. 开闭原则(OCP)
    核心思想:类对扩展开放,对修改关闭。
  2. 单一职责原则(SRP)
    核心思想:每个类只负责单一职责。
  3. 里氏代换原则(LSP)
    核心思想:子类可替换父类,且不破坏程序正确性。
  4. 合成复用原则(CRP)
    核心思想:优先使用组合 / 聚合,而非继承。
  5. 依赖倒转原则(DIP)
    核心思想:依赖抽象而非具体实现。

第一次作业

1.点线面问题重构

在“点与线(类设计)”题目基础上,对题目的类设计进行重构,以实现继承与多态的技术性需求。这题没有什么难度只需要照着题目要求设计即可

2.雨刷程序功能扩展设计

在给定的汽车手动风挡玻璃雨刷程序的基础上,对程序进行重构(Refactoring),使得程序可以对功能进行扩展。
通过控制杆 (Lever) 和刻度盘 (Dial) 来调节雨刷 (Brush) 的摆动速度。系统采用了中介者模式 (Agent) 来降低实体类之间的耦合性,整体设计清晰且符合面向对象原则。
难度同样不大

3.航空货运管理系统

点击题目
航空快递以速度快、安全性高成为急件或贵重物品的首选。本题目要求对航空货运管理系统进行类设计,具体说明参看说明文件。
OO第九周作业题目说明.pdf

输入格式:
按如下顺序分别输入客户信息、货物信息、航班信息以及订单信息。

客户编号
客户姓名
客户电话
客户地址
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为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位小数。

输入样例:
在这里给出一组输入。例如:

10001
郭靖
13807911234
南昌航空大学
2
101
发电机
80
60
40
80
102
信号发生器
55
70
60
45
MU1234
昌北国际机场
大兴国际机场
2025-04-22
1000
900001
2025-04-22
南昌大学
洪七公
18907912325
北京大学
黄药师
13607912546
输出样例:
在这里给出相应的输出。例如:

客户:郭靖(13807911234)订单信息如下:
-----------------------------------------
航班号:MU1234
订单号:900001
订单日期:2025-04-22
发件人姓名:洪七公
发件人电话:18907912325
发件人地址:南昌大学
收件人姓名:黄药师
收件人电话:13607912546
收件人地址:北京大学
订单总重量(kg):125.0
微信支付金额:3350.0

货物明细如下:
-----------------------------------------
明细编号    货物名称    计费重量    计费费率    应交运费
1    发电机    80.0    25.0    2000.0
2    信号发生器    45.0    30.0    1350.0
点击查看代码
import java.lang.*;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.List;
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 客户信息
        String customerId = scanner.nextLine();
        String customerName = scanner.nextLine();
        String customerPhone = scanner.nextLine();
        String customerAddress = scanner.nextLine();
        Customer customer = new Customer(customerId, customerName, customerPhone, customerAddress);

        // 货物信息
        int goodsCount = Integer.parseInt(scanner.nextLine());
        ArrayList<Product> goodsList = new ArrayList<>();

        for (int i = 0; i < goodsCount; i++) {
            String goodsId = scanner.nextLine();
            String goodsName = scanner.nextLine();
            double goodsWidth = Double.parseDouble(scanner.nextLine());
            double goodsLength = Double.parseDouble(scanner.nextLine());
            double goodsHeight = Double.parseDouble(scanner.nextLine());
            double goodsWeight = Double.parseDouble(scanner.nextLine());

            goodsList.add(new Product(goodsId, goodsName, goodsWidth, goodsLength, goodsHeight, goodsWeight,new Billing()));
        }

        // 航班信息
        String flightNumber = scanner.nextLine();
        String departureAirport = scanner.nextLine();
        String arrivalAirport = scanner.nextLine();
        String flightDate = scanner.nextLine();
        double maxLoad = Double.parseDouble(scanner.nextLine());
        Flight flight = new Flight(flightNumber, departureAirport, arrivalAirport, flightDate, maxLoad, goodsList);

        // 订单信息
        String orderId = scanner.nextLine();
        String orderDate = scanner.nextLine();
        String senderAddress = scanner.nextLine();
        String senderName = scanner.nextLine();
        String senderPhone = scanner.nextLine();
        Sender sender = new Sender(senderName, senderPhone, senderAddress);
        String receiverAddress = scanner.nextLine();
        String receiverName = scanner.nextLine();
        String receiverPhone = scanner.nextLine();
        Receiver receiver = new Receiver(receiverName, receiverPhone, receiverAddress);
        Payment payment = new WechatPay();
        // 创建订单并关联商品
        Order order = new Order(orderId, orderDate,goodsList);
        if(flight.isVolumeEnough()){
            Printer.printCustomerInfo(customer);
            Printer.printOrderSummary(order,flight);
            Printer.printSenderInfo(sender);
            Printer.printReceiverInfo(receiver,order);
            payment.pay(order.getTotalPrice());
            Printer.printProductDetails(goodsList);
        }
        else {
            System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.",flight.getFlightNumber());
        }


        scanner.close();
    }
}
class Order{

    private String orderId;          // 订单编号
    private String orderDate;        // 订单日期(YYYY-MM-DD)
    private ArrayList<Product> products;
    public Order(String orderId, String orderDate,  ArrayList<Product> products) {
        this.orderId = orderId;
        this.orderDate = orderDate;
        this.products = products;
    }
    public double getTotalPrice() {
        double total = 0;
        for (Product product : products) {
            total += product.getPrice();
        }
        return Math.round(total * 10) / 10.0;
    }
    public double getTotalWeight() {
        double total = 0;
        for (Product product : products) {
            total += product.calculationweight();  // 改为使用计费重量
        }
        return Math.round(total * 10) / 10.0;
    }
    public String getOrderId() {
        return orderId;
    }

    public String getOrderDate() {
        return orderDate;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public void setOrderDate(String orderDate) {
        this.orderDate = orderDate;
    }


}
class Flight {
    private String flightNumber;      // 航班号
    private String departureAirport; // 起飞机场
    private String arrivalAirport;   // 降落机场
    private String flightDate;       // 航班日期(YYYY-MM-DD)
    private double maxLoad;         // 最大载重量(kg)
    private ArrayList<Product> products;
    // 完整构造方法
    public Flight(String flightNumber, String departureAirport, String arrivalAirport, String flightDate, double maxLoad,ArrayList<Product> products) {
        this.flightNumber = flightNumber;
        this.departureAirport = departureAirport;
        this.arrivalAirport = arrivalAirport;
        this.flightDate = flightDate;
        this.maxLoad = maxLoad;
        this.products = products;
    }
    public double getTotalWeight() {
        double total = 0;
        for (Product product : products) {
            total += product.calculationweight();  // 使用计费重量而非实际重量
        }
        return Math.round(total * 10) / 10.0;
    }
    // Getter和Setter方法
    public String getFlightNumber() {
        return flightNumber;
    }

    public void setFlightNumber(String flightNumber) {
        this.flightNumber = flightNumber;
    }

    public String getDepartureAirport() {
        return departureAirport;
    }

    public void setDepartureAirport(String departureAirport) {
        this.departureAirport = departureAirport;
    }

    public String getArrivalAirport() {
        return arrivalAirport;
    }

    public void setArrivalAirport(String arrivalAirport) {
        this.arrivalAirport = arrivalAirport;
    }

    public String getFlightDate() {
        return flightDate;
    }

    public void setFlightDate(String flightDate) {
        this.flightDate = flightDate;
    }

    public double getMaxLoad() {
        return maxLoad;
    }

    public void setMaxLoad(double maxLoad) {
        this.maxLoad = maxLoad;
    }

    // 实用方法:获取航班路线信息
    public String getRoute() {
        return departureAirport + " → " + arrivalAirport;
    }
    public double getTotalVolume() {
        double total = 0;
        for (Product product : products) {
            total += product.getVolume();
        }
        return total;
    }
    public boolean isVolumeEnough() {
        return maxLoad >= getTotalWeight();
    }

    public ArrayList<Product> getProducts() {
        return products;
    }
    public void setProducts(ArrayList<Product> products) {
        this.products = products;
    }


}
class Product {
    private String id;
    private String name;
    private Double width;
    private Double length;
    private Double height;
    private Double weight;
    private double price;
    private BillingStrategy billingStrategy; // 新增计费策略

    public Product(String id, String name, Double width, Double length, Double height, Double weight, BillingStrategy billingStrategy) {
        this.id = id;
        this.name = name;
        this.width = width;
        this.length = length;
        this.height = height;
        this.weight = weight;
        this.billingStrategy = billingStrategy;
        this.price = getPrice();
    }

    public String getName() {
        return name;
    }

    public Double getHeight() {
        return height;
    }

    public String getId() {
        return id;
    }

    public BillingStrategy getBillingStrategy() {
        return billingStrategy;
    }

    public void setBillingStrategy(BillingStrategy billingStrategy) {
        this.billingStrategy = billingStrategy;
    }

    public Double getLength() {
        return length;
    }

    public Double getWeight() {
        return weight;
    }

    public Double getWidth() {
        return width;
    }

    public void setHeight(Double height) {
        this.height = height;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setLength(Double length) {
        this.length = length;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setWeight(Double weight) {
        this.weight = weight;
    }

    public void setWidth(Double width) {
        this.width = width;
    }

    public double getPrice() {
        setPrice(calculationweight() * billingStrategy.calculateRate(calculationweight()));
        return calculationweight() * billingStrategy.calculateRate(calculationweight());
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public double calculationweight() {
        double volumeCm = length * width * height; // 立方厘米
        double volumetricWeight = volumeCm / 6000.0; // 体积重量(kg)
        return Math.max(weight, volumetricWeight); // 取较大者
    }

    public double getVolume() {
        double VolumetricWeight = weight * height * width;
        return VolumetricWeight;
    }

    public boolean compare(double volumetricWeight) {
        return volumetricWeight < weight;
    }
}
// People类改进
abstract class People {
    protected String name;
    protected String phone;
    protected String address;

    public People(String name, String phone, String address) {
        this.name = name;
        this.phone = phone;
        this.address = address;
    }

    // 公共方法,所有子类都应支持
    public String getContactInfo() {
        return name + "(" + phone + ")";
    }

    // 抽象方法,强制子类实现
    public abstract String getDisplayRole();

    @Override
    public String toString() {
        return getDisplayRole() + " " + getContactInfo() + " - " + address;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

// Customer类改进
class Customer extends People {
    private String customerId;

    public Customer(String customerId, String name, String phone, String address) {
        super(name, phone, address);
        this.customerId = customerId;
    }

    @Override
    public String getDisplayRole() {
        return "客户[" + customerId + "]";
    }

    // 新增客户特有方法
    public String getCustomerId() {
        return customerId;
    }
}

// Sender类改进
class Sender extends People {
    public Sender(String name, String phone, String address) {
        super(name, phone, address);
    }

    @Override
    public String getDisplayRole() {
        return "发件人";
    }

}

// Reciver类改进
class Receiver extends People {
    public Receiver(String name, String phone, String address) {
        super(name, phone, address);
    }

    @Override
    public String getDisplayRole() {
        return "收件人";
    }
}
// 1. 定义支付接口(抽象层)
interface Payment {
    void pay(double amount);
}

// 2. 实现微信支付
class WechatPay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("微信支付金额:" + amount+"\n");
    }
}

// 3. 实现支付宝支付
class AliPay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("支付宝支付:" + amount);
    }
}
class Printer {
    // 打印客户信息
    public static void printCustomerInfo(Customer customer) {
        System.out.println("客户:" + customer.getName() + "(" + customer.getPhone() + ")订单信息如下:");
    }

    // 打印发件人信息
    public static void printSenderInfo(Sender sender) {
        System.out.println("发件人姓名:" + sender.getName());
        System.out.println("发件人电话:" + sender.getPhone());
        System.out.println("发件人地址:" + sender.getAddress());
    }

    // 打印收件人信息
    public static void printReceiverInfo(Receiver receiver,Order order) {
        System.out.println("收件人姓名:" + receiver.getName());
        System.out.println("收件人电话:" + receiver.getPhone());
        System.out.println("收件人地址:" + receiver.getAddress());
        System.out.println("订单总重量(kg):" + order.getTotalWeight());
    }

    // 打印订单概览信息
    public static void printOrderSummary(Order order,Flight flight) {
        System.out.println("-----------------------------------------");
        System.out.println("航班号:"+flight.getFlightNumber());
        System.out.println("订单号:" + order.getOrderId());
        System.out.println("订单日期:" + order.getOrderDate());
    }
    // 打印货物明细
    public static void printProductDetails(List<Product> products) {
        System.out.println("货物明细如下:\n-----------------------------------------");
        System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
        int count = 1;
        for (Product product : products) {
            System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n",
                    count++,
                    product.getName(),
                    product.calculationweight(),
                    product.getBillingStrategy().calculateRate(product.calculationweight()),
                    product.getPrice()
            );
        }
    }
}

interface BillingStrategy {
    double calculateRate(double weight);
}

class Billing implements BillingStrategy {
    @Override
    public double calculateRate(double weight) {
        if (weight < 20) {
            return 35;
        } else if (weight >= 20 && weight < 50) {
            return 30;
        } else if (weight >= 50 && weight < 100) {
            return 25;
        } else {
            return 15;
        }
    }
}

分析

这个题目要求设计一个航空货运管理系统,重点考察面向对象设计原则的应用。系统需要根据货物实际重量和体积重量中的较大值作为计费重量(体积重量=长×宽×高÷6000),并采用分段费率计算运费(重量<20kg费率35,20-50kg费率30,50-100kg费率25,≥100kg费率15)。程序需处理客户信息、多件货物信息、航班信息和支付信息,生成订单报表和货物明细报表。考核重点在于:1单一职责原则;2里氏替换原则3开闭原则4合成复用原则)。实现时需要合理设计类结构,使用策略模式处理不同计费规则和支付方式,确保系统具有良好的扩展性和维护性。

(1) Flight类
属性:包含航班号(flightNumber)、出发机场(departureAirport)、到达机场(arrivalAirport)、航班日期(flightDate)、最大载重量(maxLoad)。这些属性完整描述了航班的基本特征和运输能力,是货运业务的核心载体。
方法:构造函数用于初始化航班对象;getFlightNumber等getter方法用于获取属性值;setFlightNumber等setter方法用于修改属性;getTotalWeight计算当前货物总重量;isVolumeEnough检查载重是否足够。这些方法实现了航班信息的封装和业务逻辑处理。

(2) Order类
属性:包含订单编号(orderId)、订单日期(orderDate)、商品列表(products)。这些属性记录了订单的基本信息和关联的所有商品,是业务处理的核心单元。
方法:构造函数用于创建订单对象;getTotalPrice计算订单总金额;getTotalWeight计算订单总重量;getOrderId等getter方法获取订单信息。这些方法实现了订单数据的封装和核心业务计算。

(3) Product类
属性:包含商品ID(id)、名称(name)、宽度(width)、长度(length)、高度(height)、重量(weight)、价格(price)。这些属性详细描述了商品的物理特征和计费相关信息。
方法:构造函数初始化商品信息;calculationweight计算计费重量;getPrice获取商品运费;getVolume计算商品体积;各类getter/setter方法操作属性。这些方法实现了商品数据的封装和运费计算逻辑。

(4) People抽象类及其子类
属性:包含姓名(name)、电话(phone)、地址(address)等基础信息。这些属性是所有人员角色的公共特征。
方法:抽象类定义getContactInfo公共方法和getDisplayRole抽象方法;Customer/Sender/Receiver子类实现具体角色显示逻辑。这种设计实现了人员信息的统一管理和多态展示。

(5) Payment接口及其实现类
属性:无显式属性,作为行为契约存在。
方法:定义pay抽象方法;WechatPay/AliPay实现具体支付逻辑。这种设计支持支付方式的灵活扩展。

(6) Printer类
属性:无状态属性,作为工具类存在。
方法:提供printCustomerInfo等静态方法集中处理所有打印逻辑。这种设计遵循了单一职责原则。

(7) BillingStrategy接口及实现
属性:无显式属性,作为策略契约存在。
方法:定义calculateRate抽象方法;Billing类实现具体费率计算规则。这种设计支持计费策略的灵活变更。

2).SourceMontor的生成报表


代码规模相关

行数与语句数:代码总行数为233行,语句数量为3.0条(可能存在统计误差)。行数与语句数比例异常,表明文件中可能包含大量空白行或注释行。

方法调用:方法调用语句占比6.3%,调用频率较低,说明模块间耦合度可能较低,但功能复用性需要评估。

代码结构特征

分支语句:分支语句占比63%,显著偏高,表明代码逻辑复杂度较高,存在大量条件判断,可能影响可维护性和测试覆盖率。

注释占比:注释行占比仅5%,远低于推荐值(建议20%-30%),关键业务逻辑缺乏文档说明,将增加后续维护成本。

类与方法:包含11.60个类/接口,平均每个类有3.50个方法。类规模适中但方法较少,可能存在过度拆分问题。平均每个方法含6条语句,长度合理。

代码复杂度相关

圈复杂度:

最大圈复杂度相对适中,平均圈复杂度较低,说明大部分方法逻辑分支简单,但存在个别相对复杂方法(如ExpediteCargo.calculateRate() ),需关注复杂方法的维护和优化 。

平均复杂度未显示,但其他方法复杂度普遍较低(如calculationweight()复杂度为1)。

代码块深度:

最大块深度3层,平均深度1.17,嵌套层次控制较好。

关键复杂方法:

Main.main():复杂度21,43条语句,35次方法调用

Order.getTotalPrice()/getTotalWeight():复杂度2,包含4条语句和2次调用,相对合理

质量改进建议

紧急重构Main.main()方法,拆分为多个单一职责方法

增加注释覆盖率至20%以上,特别是复杂业务逻辑

优化高分支占比问题,考虑使用策略模式替代条件判断

检查类职责划分,3.5方法/类的比例可能指示类粒度过细

补充单元测试,重点覆盖复杂度>5的方法

总结

这次作业让我意识到,面向对象编程不仅仅是语法规则的堆砌,更是一种编程思维的转变。继承和多态作为其重要特性,能够帮助我们将现实世界中的复杂事物抽象成代码模型,使代码更具逻辑性和结构性。在未来的学习和实践中,我会更加注重对这些核心概念的运用,尝试在更多实际项目中灵活使用继承和多态,不断提升自己的编程能力和解决问题的能力。同时,我也期待在后续的学习中,探索更多 Java 语言的特性和编程技巧,进一步完善自己的知识体系,为成为一名优秀的开发者而努力。

第二次作业

点击查看题目
航空快递以速度快、安全性高成为急件或贵重物品的首选。本题目要求对航空货运管理系统进行类设计,具体说明参看说明文件。
OO第十二周作业题目说明.pdf

输入格式:
按如下顺序分别输入客户信息、货物信息、航班信息以及订单信息。

客户类型[可输入项:Individual/Corporate]
客户编号
客户姓名
客户电话
客户地址
货物类型[可输入项:Normal/Expedite/Dangerous]
运送货物数量
[货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]//[]内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为YYYY-MM-DD)
航班最大载重量
订单编号
订单日期(格式为YYYY-MM-DD)
发件人地址
发件人姓名
发件人电话
收件人地址
收件人姓名
收件人电话
支付方式[可输入项:Wechat/ALiPay/Cash]
输出格式:
如果订单中货物重量超过航班剩余载重量,程序输出The flight with flight number:航班号 has exceeded its load capacity and cannot carry the order. ,程序终止运行。
如果航班载重量可以承接该订单,输出如下:
客户:姓名(电话)订单信息如下:
-----------------------------------------
航班号:
订单号:
订单日期:
发件人姓名:
发件人电话:
发件人地址:
收件人姓名:
收件人电话:
收件人地址:
订单总重量(kg):
[微信/支付宝/现金]支付金额:

货物明细如下:
-----------------------------------------
明细编号    货物名称    计费重量    计费费率    应交运费
1    ...
2    ...
注:输出中实型数均保留1位小数。

输入样例:
在这里给出一组输入。例如:

Corporate
10001
郭靖
13807911234
南昌航空大学
Expedite
2
101
发电机
80
60
40
80
102
信号发生器
55
70
60
45
MU1234
昌北国际机场
大兴国际机场
2025-04-22
1000
900001
2025-04-22
南昌大学
洪七公
18907912325
北京大学
黄药师
13607912546
ALiPay
输出样例:
在这里给出相应的输出。例如:

客户:郭靖(13807911234)订单信息如下:
-----------------------------------------
航班号:MU1234
订单号:900001
订单日期:2025-04-22
发件人姓名:洪七公
发件人电话:18907912325
发件人地址:南昌大学
收件人姓名:黄药师
收件人电话:13607912546
收件人地址:北京大学
订单总重量(kg):125.0
支付宝支付金额:4360.0

货物明细如下:
-----------------------------------------
明细编号    货物名称    计费重量    计费费率    应交运费
1    发电机    80.0    40.0    3200.0
2    信号发生器    45.0    50.0    2250.0
点击查看代码
import java.lang.*;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.List;
//1.将打印类抽象化,加入两个子类货物明细和订单明细
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 客户信息
        String customerType = scanner.nextLine();
        String customerId = scanner.nextLine();
        String customerName = scanner.nextLine();
        String customerPhone = scanner.nextLine();
        String customerAddress = scanner.nextLine();
        Customer customer;
        if ("Corporate".equalsIgnoreCase(customerType)) {
            customer = new CorporateCustomer(customerId, customerName, customerPhone, customerAddress);
        } else {
            customer = new IndividualCustomer(customerId, customerName, customerPhone, customerAddress);
        }

        // 货物信息
        String goodsType = scanner.nextLine();
        int goodsCount = Integer.parseInt(scanner.nextLine());
        ArrayList<Product> goodsList = new ArrayList<>();
        BillingStrategy billingStrategy;
        switch (goodsType) {
            case "Normal":billingStrategy=new CommonBilling();break;
            case "Expedite":billingStrategy=new ExpediteBilling();break;
            default:billingStrategy=new DangerousBilling();break;
        }
// 货物类型处理
        for (int i = 0; i < goodsCount; i++) {
            String goodsId = scanner.nextLine();
            String goodsName = scanner.nextLine();
            double goodsWidth = Double.parseDouble(scanner.nextLine());
            double goodsLength = Double.parseDouble(scanner.nextLine());
            double goodsHeight = Double.parseDouble(scanner.nextLine());
            double goodsWeight = Double.parseDouble(scanner.nextLine());

            Product product;
            switch (goodsType) {
                case "Normal":
                    product = new CommonProduct(goodsId, goodsName, goodsWidth, goodsLength, goodsHeight, goodsWeight, billingStrategy);
                    break;
                case "Expedite":
                    product = new ExpediteProduct(goodsId, goodsName, goodsWidth, goodsLength, goodsHeight, goodsWeight, billingStrategy);
                    break;
                default:
                    product = new DangerousProduct(goodsId, goodsName, goodsWidth, goodsLength, goodsHeight, goodsWeight, billingStrategy);
            }
            goodsList.add(product);
        }

        // 航班信息
        String flightNumber = scanner.nextLine();
        String departureAirport = scanner.nextLine();
        String arrivalAirport = scanner.nextLine();
        String flightDate = scanner.nextLine();
        double maxLoad = Double.parseDouble(scanner.nextLine());
        Flight flight = new Flight(flightNumber, departureAirport, arrivalAirport, flightDate, maxLoad, goodsList);

        // 订单信息
        String orderId = scanner.nextLine();
        String orderDate = scanner.nextLine();
        String senderAddress = scanner.nextLine();
        String senderName = scanner.nextLine();
        String senderPhone = scanner.nextLine();
        Sender sender = new Sender(senderName, senderPhone, senderAddress);
        String receiverAddress = scanner.nextLine();
        String receiverName = scanner.nextLine();
        String receiverPhone = scanner.nextLine();
        Receiver receiver = new Receiver(receiverName, receiverPhone, receiverAddress);
        String payway = scanner.nextLine();
        Payment payment;
        switch (payway) {
            case "Wechat":payment=new WechatPay();break;
            case "ALiPay":payment=new AliPay();break;
            default:payment=new CashPay();break;
        }
        // 创建订单并关联商品
        Order order = new Order(orderId, orderDate,goodsList);
        if(flight.isVolumeEnough()){
            Printer printer = new Printer(); // 创建实例
            printer.printOrder(customer, sender, receiver, order, flight); // 非静态调用
            payment.pay(order.getTotalPrice(customer));
            Printer.printProductDetails(goodsList);
        }
        else {
            System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.",flight.getFlightNumber());
        }


        scanner.close();
    }
}
class Order{

    private String orderId;          // 订单编号
    private String orderDate;        // 订单日期
    private ArrayList<Product> products;
    public Order(String orderId, String orderDate,  ArrayList<Product> products) {
        this.orderId = orderId;
        this.orderDate = orderDate;
        this.products = products;
    }
    public double getTotalPrice(Customer customer) {
        double total = products.stream().mapToDouble(Product::getPrice).sum();
        return Math.round(total * customer.getDiscountRate() * 10) / 10.0;
    }
    public double getTotalWeight() {
        double total = 0;
        for (Product product : products) {
            total += product.calculationweight();  // 改为使用计费重量
        }
        return Math.round(total * 10) / 10.0;
    }
    public String getOrderId() {
        return orderId;
    }

    public String getOrderDate() {
        return orderDate;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public void setOrderDate(String orderDate) {
        this.orderDate = orderDate;
    }


}
class Flight {
    private String flightNumber;      // 航班号
    private String departureAirport; // 起飞机场
    private String arrivalAirport;   // 降落机场
    private String flightDate;       // 航班日期(YYYY-MM-DD)
    private double maxLoad;         // 最大载重量(kg)
    private List<Product> products;
    // 完整构造方法
    public Flight(String flightNumber, String departureAirport, String arrivalAirport, String flightDate, double maxLoad,List<Product> products) {
        this.flightNumber = flightNumber;
        this.departureAirport = departureAirport;
        this.arrivalAirport = arrivalAirport;
        this.flightDate = flightDate;
        this.maxLoad = maxLoad;
        this.products = products;
    }
    public double getTotalWeight() {
        double total = 0;
        for (Product product : products) {
            total += product.calculationweight();  // 使用计费重量而非实际重量
        }
        return Math.round(total * 10) / 10.0;
    }
    // Getter和Setter方法
    public String getFlightNumber() {
        return flightNumber;
    }

    public void setFlightNumber(String flightNumber) {
        this.flightNumber = flightNumber;
    }

    public String getDepartureAirport() {
        return departureAirport;
    }

    public void setDepartureAirport(String departureAirport) {
        this.departureAirport = departureAirport;
    }

    public String getArrivalAirport() {
        return arrivalAirport;
    }

    public void setArrivalAirport(String arrivalAirport) {
        this.arrivalAirport = arrivalAirport;
    }

    public String getFlightDate() {
        return flightDate;
    }

    public void setFlightDate(String flightDate) {
        this.flightDate = flightDate;
    }

    public double getMaxLoad() {
        return maxLoad;
    }

    public void setMaxLoad(double maxLoad) {
        this.maxLoad = maxLoad;
    }

    // 实用方法:获取航班路线信息
    public String getRoute() {
        return departureAirport + " → " + arrivalAirport;
    }
    public double getTotalVolume() {
        double total = 0;
        for (Product product : products) {
            total += product.getVolume();
        }
        return total;
    }
    public boolean isVolumeEnough() {
        return maxLoad >= getTotalWeight();
    }

    public List<Product> getProducts() {
        return products;
    }
    public void setProducts(ArrayList<Product> products) {
        this.products = products;
    }


}
abstract class Product {
    private String id;
    private String name;
    private Double width;
    private Double length;
    private Double height;
    private Double weight;
    private double price;
    private BillingStrategy billingStrategy; // 计费策略

    public Product(String id, String name, Double width, Double length, Double height, Double weight, BillingStrategy billingStrategy) {
        this.id = id;
        this.name = name;
        this.width = width;
        this.length = length;
        this.height = height;
        this.weight = weight;
        this.billingStrategy = billingStrategy;
        this.price = getPrice();
    }
    public Product(){}
    public String getName() {
        return name;
    }

    public Double getHeight() {
        return height;
    }

    public String getId() {
        return id;
    }

    public BillingStrategy getBillingStrategy() {
        return billingStrategy;
    }

    public void setBillingStrategy(BillingStrategy billingStrategy) {
        this.billingStrategy = billingStrategy;
    }

    public Double getLength() {
        return length;
    }

    public Double getWeight() {
        return weight;
    }

    public Double getWidth() {
        return width;
    }

    public void setHeight(Double height) {
        this.height = height;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setLength(Double length) {
        this.length = length;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setWeight(Double weight) {
        this.weight = weight;
    }

    public void setWidth(Double width) {
        this.width = width;
    }

    public double getPrice() {
        setPrice(calculationweight() * billingStrategy.calculateRate(calculationweight()));
        return calculationweight() * billingStrategy.calculateRate(calculationweight());
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public double calculationweight() {
        double volumeCm = length * width * height; // 立方厘米
        double volumetricWeight = volumeCm / 6000.0; // 体积重量(kg)
        return Math.max(weight, volumetricWeight); // 取较大者
    }//计算计费重量

    public double getVolume() {
        double VolumetricWeight = weight * height * width;
        return VolumetricWeight;
    }//计算体积

    public abstract String getProductType();

}
//普通货物
class CommonProduct extends Product {
    CommonProduct(String id, String name, Double width,Double length, Double height, Double weight, BillingStrategy billingStrategy) {
        super(id,name,width,length,height,weight,billingStrategy);
    }
    public String getProductType() {
        return "普通货物";
    }
}
//加急货物
class ExpediteProduct extends Product {
    ExpediteProduct(String id, String name, Double width,Double length, Double height, Double weight, BillingStrategy billingStrategy) {
        super(id,name,width,length,height,weight,billingStrategy);
    }
    public String getProductType() {
        return "急速货物";
    }
}
//危险货物
class DangerousProduct extends Product {
    DangerousProduct(String id, String name, Double width,Double length, Double height, Double weight, BillingStrategy billingStrategy) {
        super(id,name,width,length,height,weight,billingStrategy);
    }
    public String getProductType() {
        return "危险货物";
    }
}
abstract class People {
    protected String name;
    protected String phone;
    protected String address;

    public People(String name, String phone, String address) {
        this.name = name;
        this.phone = phone;
        this.address = address;
    }

    // 公共方法,所有子类都应支持
    public String getContactInfo() {
        return name + "(" + phone + ")";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
abstract class Customer extends People {
    private String customerId;

    public Customer(String customerId, String name, String phone, String address) {
        super(name, phone, address);
        this.customerId = customerId;
    }
    public String getCustomerId() {
        return customerId;
    }

    // 强制子类实现折扣率
    public abstract double getDiscountRate();

}
// 个人客户
class IndividualCustomer extends Customer {
    public IndividualCustomer(String customerId, String name, String phone, String address) {
        super(customerId, name, phone, address);
    }

    @Override
    public double getDiscountRate() {
        return 0.9; // 个人客户9折
    }
}

// 企业客户
class CorporateCustomer extends Customer {
    public CorporateCustomer(String customerId, String name, String phone, String address) {
        super(customerId, name, phone, address);
    }

    @Override
    public double getDiscountRate() {
        return 0.8; // 企业客户8折
    }
}
// Sender类改进
class Sender extends People {
    public Sender(String name, String phone, String address) {
        super(name, phone, address);
    }


}

// Reciver类改进
class Receiver extends People {
    public Receiver(String name, String phone, String address) {
        super(name, phone, address);
    }
}
// 1. 定义支付接口
interface Payment {
    void pay(double amount);
}

// 实现微信支付
class WechatPay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("微信支付金额:" + amount+"\n");
    }
}

// 实现支付宝支付
class AliPay implements Payment
{
    @Override
    public void pay(double amount) {
        System.out.println("支付宝支付金额:" + amount+"\n");
    }
}
//4.现金支付
class CashPay implements Payment
{
    @Override
    public void pay(double amount) {
        System.out.println("现金支付金额:" + amount+"\n");
    }
}
class Printer {
    // 打印货物明细
    public static void printProductDetails(List<Product> products) {
        System.out.println("货物明细如下:\n-----------------------------------------");
        System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
        int count = 1;
        for (Product product : products) {
            System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n",
                    count++,
                    product.getName(),
                    product.calculationweight(),
                    product.getBillingStrategy().calculateRate(product.calculationweight()),
                    product.getPrice()
            );
        }
    }
    public void printOrder(Customer customer,Sender sender,Receiver receiver,Order order,Flight flight) {
        //客户信息
        System.out.println("客户:" + customer.getName() + "(" + customer.getPhone() + ")订单信息如下:");
        //航班
        System.out.println("-----------------------------------------");
        System.out.println("航班号:"+flight.getFlightNumber());
        System.out.println("订单号:" + order.getOrderId());
        System.out.println("订单日期:" + order.getOrderDate());
        //发件人
        System.out.println("发件人姓名:" + sender.getName());
        System.out.println("发件人电话:" + sender.getPhone());
        System.out.println("发件人地址:" + sender.getAddress());
        //收件人
        System.out.println("收件人姓名:" + receiver.getName());
        System.out.println("收件人电话:" + receiver.getPhone());
        System.out.println("收件人地址:" + receiver.getAddress());
        System.out.println("订单总重量(kg):" + order.getTotalWeight());

    }
}
interface BillingStrategy {
    double calculateRate(double weight);
}
//危险货物计费策略
class DangerousBilling implements BillingStrategy {
    @Override
    public double calculateRate(double weight) {
        if (weight < 20) {
            return 80;
        } else if (weight >= 20 && weight < 50) {
            return 50;
        } else if (weight >= 50 && weight < 100) {
            return 30;
        } else {
            return 20;
        }
    }
}
//加急货物计费策略
class ExpediteBilling implements BillingStrategy {
    @Override
    public double calculateRate(double weight) {
        if (weight < 20) {
            return 60;
        } else if (weight >= 20 && weight < 50) {
            return 50;
        } else if (weight >= 50 && weight < 100) {
            return 40;
        } else {
            return 30;
        }
    }
}
//普通货物计费策略
class CommonBilling implements BillingStrategy {
    @Override
    public double calculateRate(double weight) {
        if (weight < 20) {
            return 35;
        } else if (weight >= 20 && weight < 50) {
            return 30;
        } else if (weight >= 50 && weight < 100) {
            return 25;
        } else {
            return 15;
        }
    }
}

##分析 这次迭代是在上一次的基础上增加了几个收费策略,以及几个货物类型和客户类型货物类型细分:普通货物/危险货物/加急货物(不同基础费率)用户类型分级:个人用户(9折)/集团用户(8折)计算公式升级:运费 = 计费重量 × 货物类型费率 × 用户折扣率增加现金支付方式需实现支付策略的动态切换明确要求可扩展类:用户、支付方式、货物。 ##类分析

Order 管理订单基本信息及关联商品

  • 计算订单总重量
  • 维护订单-商品关联关系
    Flight 管理航班运输能力校验载重限制
  • 计算当前货物总重量
  • 维护航班-货物关联关系
    Product 抽象基类,定义货物基础属性和行为
  • 抽象方法getProductType()
  • 策略模式实现计费规则
    CommonProduct
    ExpediteProduct
    DangerousProduct 具体货物类型实现类,分别实现普通/加急/危险货物的类型标识
  • 继承共用基础计算逻辑
    People 人员信息抽象基类 - 封装姓名/电话/地址等公共属性
  • 提供基础联系人信息格式
    Customer 抽象客户类 - 定义客户ID和折扣率抽象方法
    IndividualCustomer
    CorporateCustomer 具体客户类型实现 - 个人客户(9折)/企业客户(8折)
  • 实现模板方法模式
    Sender
    Receiver 发件人和收件人实体 - 继承People基础属性
  • 无额外业务逻辑
    BillingStrategy 计费策略接口 - 定义calculateRate()契约
    CommonBilling
    ExpediteBilling
    DangerousBilling 具体计费规则实现 - 分段费率计算逻辑
  • 危险品费率最高(80-20)
  • 普通品费率最低(35-15)
    Payment 支付方式接口 - 定义pay()方法契约
    WechatPay
    AliPay
    CashPay 具体支付方式实现 -多态实现不同支付方式
    Printer :处理所有报表打印逻辑

2).SourceMontor的生成报表

  1. 代码规模相关
    (1) 行数与语句数:代码总行数为557行,语句数量是320条。行数较多但语句数相对少些,可能存在一些空白行或长语句。这一规模对于小型项目或许合适,但随着功能扩展可能需要合理拆分。

(2) 方法调用:有64次方法调用,说明代码中不同功能模块间存在一定交互,调用频繁度适中,若调用逻辑混乱可能影响代码可读性和维护性。

  1. 代码结构特征
    (3) 分支语句:分支语句占比11.9%,比例适中,表明代码中条件判断等逻辑分支复杂度在可控范围内,代码执行路径相对清晰,理解和调试难度适中。

(4) 注释占比:注释行占比仅7.9%,过少的注释会使代码可读性变差,后续维护人员难以快速理解代码意图,尤其当业务逻辑复杂时,增加注释很有必要。

(5) 类与方法:有13个类,类中平均有6.38个方法,平均每个方法含3.80条语句。类数量适中且每个类的方法数合理,职责划分较为清晰,有利于代码的复用和维护;方法内语句数较少,便于理解方法功能。

  1. 代码复杂度相关
    (1) 圈复杂度:最大圈复杂度为2(Order.getTotalWeight()方法),平均圈复杂度1.02。圈复杂度在1-10属于相对简单可控范围,且平均值很低,说明代码逻辑分支情况总体良好,方法逻辑简单,可维护性和可测试性高。

(2) 代码块深度:最大代码块深度为4层,平均深度1.43层。最大深度表明代码中最深的嵌套层次为4层,在合理范围内;平均深度低说明整体代码嵌套情况不严重,代码结构较为扁平,利于理解和修改。

  1. 重点方法分析
    最复杂方法为Order.getTotalWeight()(复杂度2,4条语句),虽然复杂度不高但已是系统中最复杂的方法。其他方法复杂度均为1,说明整体实现较为简单直接。方法调用深度最大为3层(如calculationweight().getVolume()),调用关系清晰。

总结

这次迭代总体难点在与对各个类的规划以及设计若没有事先规划好的话,在修改时会牵扯到许多问题,这次迭代最大的难点在于前期的类结构设计和规划。如果没有做好充分的设计准备,后续修改时会引发一系列连锁问题,严重影响开发效率和质量。

三·采坑心得:

数据输入与格式处理问题,在输入包含多个字符串和数值的混合数据时,Scanner的nextLine()方法容易读取到残留的换行符,导致输入逻辑混乱。例如,在输入完货物数量(整数)后直接调用nextLine(),会读取到上一行输入的换行符而非实际字符串数据。在每次读取整数后,手动调用scanner.nextLine()消耗残留的换行符,确保后续nextLine()能正确读取字符串。

代码遵循了单一职责原则,Customer类:专注于客户信息的管理和折扣率计算,不涉及订单或货物逻辑。Cargo类:负责货物属性和基本运费计算,子类(如NormalCargo)通过重写calculateRate()实现特定费率规则。Order类:处理订单相关信息(如发货人、收货人、支付方式)和总金额计算,依赖其他类完成具体功能。

本次作业的核心任务是对首次编写的代码进行重构与扩展,需要新增人员身份类型、物品种类以及支付方式等功能模块。较于第一次从零开始搭建代码框架,这次任务虽在形式上是 “添砖加瓦”,但实际操作中却需要对原有代码结构进行深度剖析与调整。扩展过程中,最具挑战性的部分在于妥善处理不同类之间错综复杂的关系 —— 既要确保新增类与原有类的兼容性,又要避免因类间耦合度过高而导致代码逻辑混乱。在新增人员身份时,需要精准定位原有客户类的继承体系,判断是通过抽象类扩展还是新增接口来实现新身份的功能差异化;在处理物品种类时,则要细致考量不同物品的计费规则、运输限制等特性,通过多态机制在同一方法下实现差异化处理。得益于第一次代码编写的经验铺垫,第二次任务更多聚焦于 “增量式完善”—— 在原有类结构的基础上,有针对性地添加扩展点,根据不同业务场景细化处理逻辑。本次花费时间较多的点还是后续的为了满足设计原则,一直在不断的修改修改和完善,过程中的功能实现部分有了第一次做铺垫还是相对比较轻松的。

改进建议

引入工厂模式管理复杂对象的创建(如支付方式、货物类型)
使用建造者模式构建订单等复合对象
采用门面模式封装运费计算流程
提升注释覆盖率至20%以上,重点补充业务规则说明
提取Printer类的打印逻辑为独立接口
为危险货物添加专属校验器(Validator模式)

五、总结

在这次大作业中,我熟练地掌握了封装,继承与多态以及接口,懂得了如何设计类,合理地处理类与类之间的关系,在第二次的代码中我设计了二十多个类与接口,我设计的不一定完全合理,但我的思维发生了变化,不在像C语言一样把所有功能放在一起,我潜意识的会更具题目的需求设计一些类,赋予它一些合理的方法与属性。我不可能通过一次大作业就能够完全合理地把类设计出来,但我会不断的学习,将类设计的更加合理。在这次大作业学到最多的便是面向对象设计原则,在前言部分就有我对其的理解,这些原则会让代码变得更加的健壮,提高代码的可用性。
在第二次迭代的过程中,我需要对第一次代码进行大量的修改,仅仅通过增加新类是无法实现题目要求的,这显然是违背了开闭原则,所以通过这次作业我了解了开闭原则的重要性,在之后的大作业中我就需要尽量满足开闭原则,仅仅通过增加新类就可以实现题目要求,这样才能让代码变得高效,可扩展,但也需要对原来代码进行改进,提高代码效率。

posted @ 2025-05-24 20:22  无法表露  阅读(23)  评论(0)    收藏  举报