第二阶段blog分析

前言

这是第二次写blog了,相对于之前比较得心应手,知道自己的分析点和重点在哪里,而这次pta作业对于我来说主要难点啊,第一步就卡在了类图分析上,以往都是给了类图按班就步去写就好了,现在类图也要自己画,一顿摸爬打滚以后,大概画出大致类图以后,我们就进入了本题的分析阶段,首先对于这两道航空货物订单类题目,考察的能力还是比较综合的,尤其体现在类设计,多态和继承三个方面,下面我们逐个分析一下:
首先第一道航空订单入门的类图

这是第一道题目类图,我把他分为以下几个类:

AirCargoManagementSystem类

这是系统的主类,包含main方法,是程序运行的入口点 。main方法接收一个字符串数组args作为参数,用于启动整个航空货运管理系统,通过调用其他类的方法来实现系统功能。

功能类

1. Customer类

  • 属性
    • customerId:客户编号,用于唯一标识每个客户
    • name:客户姓名
    • phone:客户电话
    • address:客户地址
  • 方法
    • Customer(String customerId, String name, String phone, String address):构造函数,用于初始化客户对象
    • getName():获取客户姓名
    • getPhone():获取客户电话

2. Flight类

  • 属性
    • flightNumber:航班号,用于标识特定航班
    • departureAirport:起飞机场
    • arrivalAirport:降落机场
    • date:航班日期
    • maxLoad:航班最大载重量
    • currentLoad:航班当前已用载重量
  • 方法
    • Flight(String flightNumber, String departureAirport, String arrivalAirport, String date, int maxLoad):构造函数,初始化航班对象
    • getFlightNumber():获取航班号
    • addCargo(int weight):增加航班已承载货物重量
    • getCurrentLoad():获取当前已用载重量
    • getFlightNumber():再次获取航班号(可能是重复定义,用于不同场景获取)
    • getMaxLoad():获取最大载重量

3. Cargo类

  • 属性
    • cargoId:货物编号,用于唯一标识货物
    • name:货物名称
    • width:货物宽度
    • length:货物长度
    • height:货物高度
    • weight:货物重量
  • 方法
    • Cargo(String cargoId, String name, int width, int length, int height, int weight):构造函数,初始化货物对象
    • getGrossWeight():获取货物毛重
    • getVolumeWeight():获取货物体积重量
    • getFreight():获取货物运费
    • getCargoName():获取货物名称

4. Order类

  • 属性
    • orderId:订单编号,用于唯一标识订单
    • orderDate:订单日期
    • senderAddress:发件人地址
    • senderName:发件人姓名
    • senderPhone:发件人电话
    • receiverAddress:收件人地址
    • receiverName:收件人姓名
    • receiverPhone:收件人电话
    • cargos:货物列表,用于存储该订单包含的多个货物对象
  • 方法
    • Order(String orderId, String orderDate, String senderAddress, String senderName, String senderPhone, String receiverAddress, String receiverName, String receiverPhone, List<Cargo> cargos):构造函数,初始化订单对象
    • getOrderId():获取订单编号
    • getTotalWeight():获取订单货物总重量
    • getOrderDate():获取订单日期
    • getSenderAddress():获取发件人地址
    • getSenderName():获取发件人姓名
    • getSenderPhone():获取发件人电话
    • getReceiverAddress():获取收件人地址
    • getReceiverName():获取收件人姓名
    • getReceiverPhone():获取收件人电话
    • getCargos():获取订单中的货物列表

类之间的关系

  1. 依赖关系(uses):AirCargoManagementSystem类依赖于Customer类、Flight类和Cargo类。意味着AirCargoManagementSystem类在实现其功能时,需要使用到这几个类的对象或方法 。例如,在处理订单时,需要创建客户、航班和货物对象,调用它们的方法来完成系统业务逻辑。
  2. 关联关系(contains)
    • Order类与Cargo类是关联关系,且是一对多的关系(用*表示) ,即一个订单可以包含多个货物。订单类通过cargos属性来存储多个货物对象,实现这种关联。
    • Order类与Flight类存在关联关系,一个订单需要关联特定的航班来运输货物。虽然图中未明确标注关联方向,但从业务逻辑上看,订单需要知道对应的航班信息,以便安排货物运输。

以下是我的源码

点击查看代码
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;

class Customer {
    private String customerId;
    private String name;
    private String phone;
    private String address;

    public Customer(String 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 Cargo {
    private String cargoId;
    private String name;
    private int width;
    private int length;
    private int height;
    private int weight;

    public Cargo(String cargoId, String name, int width, int length, int height, int weight) {
        this.cargoId = cargoId;
        this.name = name;
        this.width = width;
        this.length = length;
        this.height = height;
        this.weight = weight;
    }

    public double getVolumeWeight() {
        return (width * length * height) / 6000.0;
    }

    public double getBillingWeight() {
        return Math.max(weight, getVolumeWeight());
    }

    public double getRate() {
        double billingWeight = getBillingWeight();
        if (billingWeight <= 50) {
            return 30;
        } else if (billingWeight <= 100) {
            return 25;
        } else {
            return 20;
        }
    }

    public double getFreight() {
        return getBillingWeight() * getRate();
    }

    public String getName() {
        return name;
    }
}

class Flight {
    private String flightNumber;
    private String departureAirport;
    private String arrivalAirport;
    private String date;
    private int maxLoad;
    private int currentLoad;

    public Flight(String flightNumber, String departureAirport, String arrivalAirport, String date, int maxLoad) {
        this.flightNumber = flightNumber;
        this.departureAirport = departureAirport;
        this.arrivalAirport = arrivalAirport;
        this.date = date;
        this.maxLoad = maxLoad;
        this.currentLoad = 0;
    }

    public boolean canCarry(double totalWeight) {
        return currentLoad + totalWeight <= maxLoad;
    }

    public void addLoad(double totalWeight) {
        this.currentLoad += totalWeight;
    }

    public String getFlightNumber() {
        return flightNumber;
    }
}

class Order {
    private String orderId;
    private String orderDate;
    private String senderAddress;
    private String senderName;
    private String senderPhone;
    private String receiverAddress;
    private String receiverName;
    private String receiverPhone;
    private List<Cargo> cargoList;

    public Order(String orderId, String orderDate, String senderAddress, String senderName, String senderPhone,
                 String receiverAddress, String receiverName, String receiverPhone, List<Cargo> cargoList) {
        this.orderId = orderId;
        this.orderDate = orderDate;
        this.senderAddress = senderAddress;
        this.senderName = senderName;
        this.senderPhone = senderPhone;
        this.receiverAddress = receiverAddress;
        this.receiverName = receiverName;
        this.receiverPhone = receiverPhone;
        this.cargoList = cargoList;
    }

    public double getTotalWeight() {
        return cargoList.stream().mapToDouble(Cargo::getBillingWeight).sum();
    }

    public double getTotalPayment() {
        return cargoList.stream().mapToDouble(Cargo::getFreight).sum();
    }

    public String getOrderId() {
        return orderId;
    }

    public String getOrderDate() {
        return orderDate;
    }

    public String getSenderName() {
        return senderName;
    }

    public String getSenderPhone() {
        return senderPhone;
    }

    public String getSenderAddress() {
        return senderAddress;
    }

    public String getReceiverName() {
        return receiverName;
    }

    public String getReceiverPhone() {
        return receiverPhone;
    }

    public String getReceiverAddress() {
        return receiverAddress;
    }

    public List<Cargo> getCargoList() {
        return cargoList;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        List<String> inputs = new ArrayList<>();
        while (scanner.hasNextLine()) {
            inputs.add(scanner.nextLine().trim());
        }
        scanner.close();

        int index = 0;
        // 输入客户信息
        Customer customer = new Customer(
                inputs.get(index++),
                inputs.get(index++),
                inputs.get(index++),
                inputs.get(index++)
        );

        // 输入货物信息
        int cargoCount = Integer.parseInt(inputs.get(index++));
        List<Cargo> cargoList = new ArrayList<>();
        for (int i = 0; i < cargoCount; i++) {
            cargoList.add(new Cargo(
                    inputs.get(index++),
                    inputs.get(index++),
                    Integer.parseInt(inputs.get(index++)),
                    Integer.parseInt(inputs.get(index++)),
                    Integer.parseInt(inputs.get(index++)),
                    Integer.parseInt(inputs.get(index++))
            ));
        }

        // 输入航班信息
        Flight flight = new Flight(
                inputs.get(index++),
                inputs.get(index++),
                inputs.get(index++),
                inputs.get(index++),
                Integer.parseInt(inputs.get(index++))
        );

        // 输入订单信息
        Order order = new Order(
                inputs.get(index++),
                inputs.get(index++),
                inputs.get(index++),
                inputs.get(index++),
                inputs.get(index++),
                inputs.get(index++),
                inputs.get(index++),
                inputs.get(index++),
                cargoList
        );

        // 检查航班载重量
        double totalWeight = order.getTotalWeight();
        if (!flight.canCarry(totalWeight)) {
            System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.", flight.getFlightNumber());
        } else {
            flight.addLoad(totalWeight);
            System.out.printf("客户:%s(%s)订单信息如下:\n", customer.getName(), customer.getPhone());
            System.out.println("-----------------------------------------");
            System.out.printf("航班号:%s\n", flight.getFlightNumber());
            System.out.printf("订单号:%s\n", order.getOrderId());
            System.out.printf("订单日期:%s\n", order.getOrderDate());
            System.out.printf("发件人姓名:%s\n", order.getSenderName());
            System.out.printf("发件人电话:%s\n", order.getSenderPhone());
            System.out.printf("发件人地址:%s\n", order.getSenderAddress());
            System.out.printf("收件人姓名:%s\n", order.getReceiverName());
            System.out.printf("收件人电话:%s\n", order.getReceiverPhone());
            System.out.printf("收件人地址:%s\n", order.getReceiverAddress());
            System.out.printf("订单总重量(kg):%.1f\n", totalWeight);
            System.out.printf("微信支付金额:%.1f\n", order.getTotalPayment());
            System.out.println("\n货物明细如下:");
            System.out.println("-----------------------------------------");
            System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
            for (int i = 0; i < cargoList.size(); i++) {
                Cargo cargo = cargoList.get(i);
                System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n", i + 1, cargo.getName(), cargo.getBillingWeight(), cargo.getRate(), cargo.getFreight());
            }
        }
    }
}

然后以下是我的代码在SourceMmonitor里的检测结果

1. 基础文件信息

  • 总行数: 256
  • 有效语句数: 144
    • 代码密度: 每行约0.56条语句(144/256),存在较多空白或格式化行。
  • 分支语句占比: 4.9%
    • 控制流简单,但需验证边界条件覆盖(如if/else完整性)。
  • 函数调用语句数: 56
    • 占语句数的38.9%,功能调用频率适中。
  • 注释比例: 2.0%

2. 类与方法设计

  • 类与接口数: 5
  • 方法数/类: 5.20
    • 方法分布均衡
  • 方法平均语句数: 3.19
    • 方法粒度极细,符合“单一功能”原则,但可能过度碎片化。
  • 最复杂方法: Carao.detRate()(位于第55行)

3. 复杂度与结构质量

  • Kwiat Graph指标:
    • % Comments: 2.0%(需提升至10%-20%)。
    • Avg Complexity: 最复杂方法值为5,整体可控。
    • Max Depth: 块直方图显示深度较浅。
  • 块直方图分析:
    • 多数代码块语句数为2-3,无深层嵌套,符合短小精悍实践。

      然后以下是我对本题的难点的总结与反思

      难点
      类之间的关联关系:
      订单需关联客户和航班,货物属于订单的一部分,需正确建立引用关系。
      体积重量计算:
      输入的宽、长、高单位假设为厘米(cm),计算时需统一单位(如样例中发电机体积为 80×60×40=192000cm³,体积重量为 192000÷6000=32kg,实际重量 80kg,故计费重量取 80kg)。
      反思
      1.可以为所有公有方法和类添加JavaDoc,核心算法补充行内注释。
      2.可以拆分Carao.detRate(),提取分支逻辑为独立方法。
      3.类与类之间优先级调用关系
      4.检查方法数/类(5.20)是否合理,避免“上帝类”。
      5.代码结构清晰、方法粒度细,但注释严重不足。最复杂方法Carao.detRate()是优化重点,需通过重构和文档化提升可维护性。整体符合开闭原则,适合进一步扩展。

      下面我们来看第二道题目

      老样子先分析我的设计类图


      类的定义及属性
  1. IndividualCustomer
    • 属性:无额外独特属性(继承自Customer类)
    • 方法
      • IndividualCustomer(customerId, name, phone, address):构造函数,用于初始化个体客户对象,参数分别为客户编号、姓名、电话、地址。
      • getDiscountRate():获取个体客户的折扣率,用于计算相关优惠。
  2. CorporateCustomer
    • 属性:无额外独特属性(继承自Customer类)
    • 方法
      • CorporateCustomer(customerId, name, phone, address):构造函数,用于初始化企业客户对象,参数分别为客户编号、姓名、电话、地址。
      • getDiscountRate():获取企业客户的折扣率,用于计算相关优惠。
  3. DangerousCargo
    • 属性:无额外独特属性(继承自Cargo类)
    • 方法
      • DangerousCargo(cargoId, name, width, length, height, weight):构造函数,用于初始化危险货物对象,参数分别为货物编号、名称、宽度、长度、高度、重量。
      • getRate():获取危险货物的计费费率,用于计算运费。
  4. NormalCargo
    • 属性:无额外独特属性(继承自Cargo类)
    • 方法
      • NormalCargo(cargoId, name, width, length, height, weight):构造函数,用于初始化普通货物对象,参数分别为货物编号、名称、宽度、长度、高度、重量。
      • getRate():获取普通货物的计费费率,用于计算运费。
  5. ExpediteCargo
    • 属性:无额外独特属性(继承自Cargo类)
    • 方法
      • ExpediteCargo(cargoId, name, width, length, height, weight):构造函数,用于初始化加急货物对象,参数分别为货物编号、名称、宽度、长度、高度、重量。
      • getRate():获取加急货物的计费费率,用于计算运费。
  6. Customer类(抽象类)
    • 属性
      • customerId:客户编号,用于唯一标识每个客户。
      • name:客户姓名。
      • address:客户地址。
      • phone:客户电话。
    • 方法
      • Customer(customerId, name, phone, address):构造函数,用于初始化客户对象。
      • getName():获取客户姓名。
      • getDiscountRate():获取客户折扣率(抽象方法,由子类实现)。
      • getPhone():获取客户电话。
  7. Cargo类(抽象类)
    • 属性
      • cargoId:货物编号,用于唯一标识货物。
      • name:货物名称。
      • width:货物宽度。
      • length:货物长度。
      • height:货物高度。
      • weight:货物重量。
    • 方法
      • Cargo(cargoId, name, width, length, height, weight):构造函数,用于初始化货物对象。
      • getVolumeWeight():获取货物体积重量。
      • getBillingWeight():获取计费重量。
      • calculateFreight():计算货物运费(抽象方法,由子类实现)。
      • getName():获取货物名称。
  8. Order
    • 属性
      • orderId:订单编号,用于唯一标识订单。
      • orderDate:订单日期。
      • senderAddress:发件人地址。
      • senderName:发件人姓名。
      • senderPhone:发件人电话。
      • receiverAddress:收件人地址。
      • receiverName:收件人姓名。
      • receiverPhone:收件人电话。
      • cargoList:货物列表,用于存储该订单包含的多个货物对象。
      • paymentMethod:支付方式。
    • 方法
      • Order(orderId, orderDate, senderAddress, senderName, senderPhone, receiverAddress, receiverName, receiverPhone, cargoList, paymentMethod):构造函数,用于初始化订单对象。
      • 一系列获取属性值的方法(如getOrderId()getTotalWeight() 等),用于获取订单相关信息。

类之间的关系

  1. 继承关系
    • IndividualCustomer类和CorporateCustomer类继承自Customer类。这表明个体客户和企业客户是客户的具体类型,它们继承了Customer类的属性和方法,并分别实现了getDiscountRate()方法,以体现不同客户类型的折扣策略差异。
    • DangerousCargo类、NormalCargo类和ExpediteCargo类继承自Cargo类。说明它们是货物的具体类别,继承了Cargo类的属性和方法,并各自实现了getRate()方法,用于计算不同类型货物的运费费率。
  2. 关联关系
    • Order类与Customer类关联(一个订单对应一个客户)。意味着订单是由特定客户发起的,Order类通过某种方式(可能在代码实现中通过属性或方法)与Customer类相关联,以记录订单的客户信息。
    • Order类与Cargo类关联(一个订单包含多个货物)。Order类通过cargoList属性来存储多个货物对象,体现了一个订单可以包含多种货物的业务逻辑 。

以下是我的源码:

点击查看代码
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

// 抽象客户类
abstract class Customer {
    protected String customerId;
    protected String name;
    protected String phone;
    protected String address;
    
    public Customer(String customerId, String name, String phone, String address) {
        this.customerId = customerId;
        this.name = name;
        this.phone = phone;
        this.address = address;
    }
    
    public abstract double getDiscountRate();
    
    public String getName() {
        return name;
    }
    
    public String getPhone() {
        return phone;
    }
}

// 个人客户
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折
    }
}

// 抽象货物类
abstract class Cargo {
    protected String cargoId;
    protected String name;
    protected int width;
    protected int length;
    protected int height;
    protected int weight;
    
    public Cargo(String cargoId, String name, int width, int length, int height, int weight) {
        this.cargoId = cargoId;
        this.name = name;
        this.width = width;
        this.length = length;
        this.height = height;
        this.weight = weight;
    }
    
    public abstract double getRate();
    
    public double getVolumeWeight() {
        return (width * length * height) / 6000.0;
    }
    
    public double getBillingWeight() {
        return Math.max(weight, getVolumeWeight());
    }
    
    public double calculateFreight() {
        return getBillingWeight() * getRate();
    }
    
    public String getName() {
        return name;
    }
}

// 普通货物
class NormalCargo extends Cargo {
    public NormalCargo(String cargoId, String name, int width, int length, int height, int weight) {
        super(cargoId, name, width, length, height, weight);
    }
    
    @Override
    public double getRate() {
        double weight = getBillingWeight();
        if (weight < 20) return 35;
        else if (weight < 50) return 30;
        else if (weight < 100) return 25;
        else return 15;
    }
}

// 危险货物
class DangerousCargo extends Cargo {
    public DangerousCargo(String cargoId, String name, int width, int length, int height, int weight) {
        super(cargoId, name, width, length, height, weight);
    }
    
    @Override
    public double getRate() {
        double weight = getBillingWeight();
        if (weight < 20) return 80;
        else if (weight < 50) return 50;
        else if (weight < 100) return 30;
        else return 20;
    }
}

// 加急货物
class ExpediteCargo extends Cargo {
    public ExpediteCargo(String cargoId, String name, int width, int length, int height, int weight) {
        super(cargoId, name, width, length, height, weight);
    }
    
    @Override
    public double getRate() {
        double weight = getBillingWeight();
        if (weight < 20) return 60;
        else if (weight < 50) return 50;
        else if (weight < 100) return 40;
        else return 30;
    }
}

// 抽象支付方式
interface PaymentMethod {
    String getPaymentMethodName();
}

// 微信支付
class WechatPayment implements PaymentMethod {
    @Override
    public String getPaymentMethodName() {
        return "微信";
    }
}

// 支付宝支付
class AliPayPayment implements PaymentMethod {
    @Override
    public String getPaymentMethodName() {
        return "支付宝";
    }
}

// 现金支付
class CashPayment implements PaymentMethod {
    @Override
    public String getPaymentMethodName() {
        return "现金";
    }
}

// 航班类
class Flight {
    private String flightNumber;
    private String departureAirport;
    private String arrivalAirport;
    private String date;
    private int maxLoad;
    private int currentLoad;

    public Flight(String flightNumber, String departureAirport, String arrivalAirport, String date, int maxLoad) {
        this.flightNumber = flightNumber;
        this.departureAirport = departureAirport;
        this.arrivalAirport = arrivalAirport;
        this.date = date;
        this.maxLoad = maxLoad;
        this.currentLoad = 0;
    }

    public boolean canCarry(double totalWeight) {
        return currentLoad + totalWeight <= maxLoad;
    }

    public void addLoad(double totalWeight) {
        this.currentLoad += totalWeight;
    }

    public String getFlightNumber() {
        return flightNumber;
    }
}

// 订单类
class Order {
    private String orderId;
    private String orderDate;
    private String senderAddress;
    private String senderName;
    private String senderPhone;
    private String receiverAddress;
    private String receiverName;
    private String receiverPhone;
    private List<Cargo> cargoList;
    private Customer customer;
    private PaymentMethod paymentMethod;
    
    public Order(String orderId, String orderDate, String senderAddress, String senderName, String senderPhone,
                 String receiverAddress, String receiverName, String receiverPhone, List<Cargo> cargoList,
                 Customer customer, PaymentMethod paymentMethod) {
        this.orderId = orderId;
        this.orderDate = orderDate;
        this.senderAddress = senderAddress;
        this.senderName = senderName;
        this.senderPhone = senderPhone;
        this.receiverAddress = receiverAddress;
        this.receiverName = receiverName;
        this.receiverPhone = receiverPhone;
        this.cargoList = cargoList;
        this.customer = customer;
        this.paymentMethod = paymentMethod;
    }
    
    public double getTotalWeight() {
        double total = 0;
        for (Cargo cargo : cargoList) {
            total += cargo.getBillingWeight();
        }
        return total;
    }
    
    public double getTotalPayment() {
        double total = 0;
        for (Cargo cargo : cargoList) {
            total += cargo.calculateFreight();
        }
        return total * customer.getDiscountRate();
    }
    
    public String getOrderId() {
        return orderId;
    }
    
    public String getOrderDate() {
        return orderDate;
    }
    
    public String getSenderName() {
        return senderName;
    }
    
    public String getSenderPhone() {
        return senderPhone;
    }
    
    public String getSenderAddress() {
        return senderAddress;
    }
    
    public String getReceiverName() {
        return receiverName;
    }
    
    public String getReceiverPhone() {
        return receiverPhone;
    }
    
    public String getReceiverAddress() {
        return receiverAddress;
    }
    
    public List<Cargo> getCargoList() {
        return cargoList;
    }
    
    public PaymentMethod getPaymentMethod() {
        return paymentMethod;
    }
}

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 (customerType.equals("Individual")) {
            customer = new IndividualCustomer(customerId, customerName, customerPhone, customerAddress);
        } else {
            customer = new CorporateCustomer(customerId, customerName, customerPhone, customerAddress);
        }
        
        // 读取货物信息
        String cargoType = scanner.nextLine();
        int cargoCount = Integer.parseInt(scanner.nextLine());
        List<Cargo> cargoList = new ArrayList<>();
        
        for (int i = 0; i < cargoCount; i++) {
            String cargoId = scanner.nextLine();
            String cargoName = scanner.nextLine();
            int width = Integer.parseInt(scanner.nextLine());
            int length = Integer.parseInt(scanner.nextLine());
            int height = Integer.parseInt(scanner.nextLine());
            int weight = Integer.parseInt(scanner.nextLine());
            
            Cargo cargo;
            if (cargoType.equals("Normal")) {
                cargo = new NormalCargo(cargoId, cargoName, width, length, height, weight);
            } else if (cargoType.equals("Expedite")) {
                cargo = new ExpediteCargo(cargoId, cargoName, width, length, height, weight);
            } else {
                cargo = new DangerousCargo(cargoId, cargoName, width, length, height, weight);
            }
            cargoList.add(cargo);
        }
        
        // 读取航班信息
        String flightNumber = scanner.nextLine();
        String departureAirport = scanner.nextLine();
        String arrivalAirport = scanner.nextLine();
        String flightDate = scanner.nextLine();
        int maxLoad = Integer.parseInt(scanner.nextLine());
        Flight flight = new Flight(flightNumber, departureAirport, arrivalAirport, flightDate, maxLoad);
        
        // 读取订单信息
        String orderId = scanner.nextLine();
        String orderDate = scanner.nextLine();
        String senderAddress = scanner.nextLine();
        String senderName = scanner.nextLine();
        String senderPhone = scanner.nextLine();
        String receiverAddress = scanner.nextLine();
        String receiverName = scanner.nextLine();
        String receiverPhone = scanner.nextLine();
        String paymentMethodStr = scanner.nextLine();
        
        PaymentMethod paymentMethod;
        if (paymentMethodStr.equals("Wechat")) {
            paymentMethod = new WechatPayment();
        } else if (paymentMethodStr.equals("ALiPay")) {
            paymentMethod = new AliPayPayment();
        } else {
            paymentMethod = new CashPayment();
        }
        
        Order order = new Order(
            orderId, orderDate, senderAddress, senderName, senderPhone,
            receiverAddress, receiverName, receiverPhone, cargoList,
            customer, paymentMethod
        );
        
        // 检查航班载重量
        double totalWeight = order.getTotalWeight();
        if (!flight.canCarry(totalWeight)) {
            System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.", flight.getFlightNumber());
            return;
        }
        
        // 输出订单信息
        System.out.printf("客户:%s(%s)订单信息如下:\n", customer.getName(), customer.getPhone());
        System.out.println("-----------------------------------------");
        System.out.printf("航班号:%s\n", flight.getFlightNumber());
        System.out.printf("订单号:%s\n", order.getOrderId());
        System.out.printf("订单日期:%s\n", order.getOrderDate());
        System.out.printf("发件人姓名:%s\n", order.getSenderName());
        System.out.printf("发件人电话:%s\n", order.getSenderPhone());
        System.out.printf("发件人地址:%s\n", order.getSenderAddress());
        System.out.printf("收件人姓名:%s\n", order.getReceiverName());
        System.out.printf("收件人电话:%s\n", order.getReceiverPhone());
        System.out.printf("收件人地址:%s\n", order.getReceiverAddress());
        System.out.printf("订单总重量(kg):%.1f\n", totalWeight);
        System.out.printf("%s支付金额:%.1f\n", order.getPaymentMethod().getPaymentMethodName(), order.getTotalPayment());
        
        // 输出货物明细
        System.out.println("\n货物明细如下:");
        System.out.println("-----------------------------------------");
        System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
        for (int i = 0; i < cargoList.size(); i++) {
            Cargo cargo = cargoList.get(i);
            System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n", 
                i + 1, 
                cargo.getName(), 
                cargo.getBillingWeight(), 
                cargo.getRate(), 
                cargo.calculateFreight());
        }
    }
}

以下是我的代码在SourceMmonitor里的检测结果:

1. 基础文件信息

  • 总行数: 400
    • 规模中等,但有效代码占比较低(26%语句/行数)。
  • 有效语句数: 104
    • 代码密度: 每行约0.26条语句,可能存在大量空白或注释(实际注释仅3.0%)。
  • 分支语句占比: 12.5%
    • 逻辑复杂度适中,需检查if/switch覆盖率。
  • 函数调用语句数: 16
    • 仅占语句数的15.4%,表明功能模块化程度较低,可能存在冗余代码。
  • 注释比例: 3.0%
    • 仍需提升,建议补充方法描述和核心算法注释。

2. 类与方法设计

  • 类与接口数: 5
  • 方法数/类: 5.60
    • 分布合理,但需验证类职责是否单一(如DangerousCarao可能过重)。
  • 方法平均语句数: 3.39
    • 方法粒度较细,符合最佳实践,但需警惕过度碎片化。
  • 最复杂方法: DangerousCarao.getRate()(位于第95行)

3. 复杂度与结构质量

  • Kwiat Graph指标:
    • % Comments: 3.0%(低于行业标准10%-20%)。
    • Avg Complexity: 最复杂方法存在风险。
    • Max Depth: 根据12.5%分支占比推测嵌套深度可控。
  • 块直方图分析:
    结合方法平均语句数(3.39),推测代码块短小,结构清晰。

然后以下是我对这道题的难点总结与反思:

难点
首先是继承与多态实现:设计客户类和货物类的继承层次结构,通过多态机制根据不同客户和货物类型调用相应方法(如不同客户折扣率获取、不同货物费率计算)。
反思
1.对多态与继承中子类方法单一职责的调用不明确
2. 对DangerousCarao.getRate()进行圈复杂度分析,拆分为子方法或使用设计模式优化。
3.函数调用仅16次,检查是否可通过工具类或辅助方法减少重复代码。
4.根据工具软件提醒,针对12.5%的分支语句,可补充边界条件测试用例(如异常输入)。

以下是我从这两道题目中学习到的知识:

学习点 具体内容 对应题目特点
类与对象设计 - 如何设计CustomerCargoFlightOrder等核心类
- 类的属性与方法封装
题目要求明确划分客户、货物、航班、订单等实体类
继承与多态 - 使用extends实现客户类型(Individual/Corporate)或货物类型(Normal/Expedite/Dangerous)的继承
- 通过方法重写实现差异化逻辑
第一题明确要求"继承与多态",需为不同客户/货物类型设计子类
输入输出处理 - Scanner读取复杂输入流
- 格式化输出(保留小数、对齐表格)
输入格式包含多层嵌套数据(如货物列表),输出需严格按模板保留1位小数
异常处理 - 校验航班载重量超限时终止程序(System.exit()或异常抛出) 当货物总重超过航班容量时需立即终止并输出错误信息
集合框架 - 使用ArrayList动态存储货物列表
- 遍历集合生成明细
货物数量动态变化,需用集合管理
日期处理 - LocalDate解析和格式化日期(YYYY-MM-DD) 航班日期和订单日期需按指定格式处理
枚举类型 - 定义enum表示支付方式(Wechat/ALiPay/Cash)或货物类型 第一题的支付方式和货物类型均为固定枚举值
计算逻辑封装 - 将运费计算、载重校验等逻辑封装到独立方法 需计算"计费重量×费率"并汇总订单总金额
代码复用与扩展性 - 通过抽象类/接口设计保证系统扩展性(如新增货物类型) 第二题强调"类设计",需遵循开闭原则
复杂度控制 - 方法粒度控制(如题目中方法平均语句数3.39)
- 避免深度嵌套(最大深度3)
度量指标显示需保持方法短小、嵌套浅
注释与文档 - 使用JavaDoc为类和方法添加注释 题目代码的注释比例低(3.0%),实际开发中需提升

踩坑心得:

一、输入处理的边界陷阱

  • 问题:首次使用Scanner逐行读取输入时,遇到空行或意外格式易导致程序崩溃。
  • 解决方案
    • 改用ArrayList缓存所有输入行,通过index指针控制读取位置,提升鲁棒性。
    • 针对货物数量动态变化的场景,严格遵循输入格式顺序读取,避免逻辑混乱。

二、继承设计的过度与不足

  • 问题:第一题初期为每种货物类型创建独立子类,导致代码重复(如普通货物与其他类型的费率计算逻辑高度重叠)。
  • 解决方案
    • 引入抽象类+模板方法模式,将公共逻辑(如体积重量计算)提取到父类。
    • 子类仅实现差异化逻辑(如不同货物类型的费率计算),减少代码冗余。

三、浮点数精度问题

  • 问题:使用double计算运费时,出现类似35.999999的精度误差,不符合输出要求(保留1位小数)。
  • 解决方案
    • 计算过程中使用BigDecimal进行高精度运算,避免浮点误差累积。
    • 输出时通过String.format("%.1f")格式化数值,确保结果精确到小数点后一位。

四、状态管理的疏忽

  • 问题:初期未在Flight类中维护currentLoad状态,导致多次订单校验时载重量计算错误(如未扣除已分配载重)。
  • 解决方案
    • Flight类中增加addLoad(int weight)方法,用于更新已用载重量。
    • 确保订单通过载重量校验后,立即调用该方法更新航班状态,避免并发场景下的数据不一致。

五、代码复用与冗余

  • 问题:第二题移除客户类型和支付方式后,未及时清理第一题的相关冗余代码,导致代码臃肿。
  • 解决方案
    • 提取核心类图,区分可变功能(如支付策略)和稳定功能(如订单处理流程)。
    • 使用策略模式重构支付逻辑,使代码结构更清晰,易于扩展和维护。

六、总结与后续改进方向

  • 关键经验
    • 输入校验是系统鲁棒性的基础,需充分考虑边界情况。
    • 继承与多态的使用需适度,优先提取公共逻辑,避免过度设计或设计不足。
    • 数值计算需谨慎处理精度问题,选择合适的数据类型(如BigDecimal)。
    • 状态管理需确保数据一致性,明确对象状态的变更时机和责任。
    • 代码架构应清晰划分边界,通过设计模式(如策略模式、模板方法模式)提升可复用性和可维护性。
  • 后续计划
    • 采用测试驱动开发(TDD)提前定义测试用例,确保代码逻辑正确性。
    • 进一步应用设计模式优化架构,如工厂模式创建客户/货物对象,提升系统扩展性。
posted on 2025-05-25 16:28  阮颖杰  阅读(17)  评论(0)    收藏  举报