航空货运管理系统分析
一、前言
题目集8和题目集9主要考察知识点:在第八次题目集中,最主要的是类设计,需要符合单一职责原则,里氏代换原则,开闭原则,合成复用原则。第九次题目集中则需要用到继承与多态等知识点,需要用到抽象类。这次俩题的题量不是很大,没有复杂的算法,其中的逻辑也比较简单易懂,更多的是需要我们进行对类图的设计以及类的分类等等。其中第八次题目集中的题量并不是很大,只需思考类设计就行,而第九次题目集在第八次题目集的基础上只添加的继承与多态的知识点,只要把类图准确的设计出来,对代码的编译上会比较简单。对于此次大作业的难度相比于上次更加简单,没有过多复杂的算法设计,只需要简单的类分类以及设计,然后要求满足四个基本原则,需要按照题目的要求进行设计。多一点细心都能够做出来。
二、设计与分析
1. 题目集08中“航空货运管理系统”:
设计要求:
一、计费重量的确定
空运以实际重量(Gross Weight)和体积重量(Volume Weight)中的较
高者作为计费重量。
计算公式:
体积重量(kg) = 货物体积(长×宽×高,单位:厘米)÷ 6000
示例:
若货物实际重量为 80kg,体积为 120cm×80cm×60cm,则:
体积重量 = (120×80×60) ÷ 6000 = 96kg
计费重量取 96kg(因 96kg > 80kg)。
二、基础运费计算
费率(Rate):航空公司或货代根据航线、货物类型、市场行情等制定(如
CNY 30/kg)。本次作业费率采用分段计算方式:
𝐑𝐚te =
𝟑5 重量 < 𝟐0
30 𝟐0 ≤ 重量 < 50
25 50 ≤ 重量 < 100
15 重量 ≥ 100
公式:基础运费 = 计费重量 × 费率
三、题目说明
本次题目模拟某客户到该航空公司办理一次货运业务的过程:
航空公司提供如下信息:
航班信息(航班号,航班起飞机场所在城市,航班降落机场所在城市,航班
日期,航班最大载重量)
客户填写货运订单并进行支付,需要提供如下信息:
客户信息(姓名,电话号码等)
货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)
运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选
航班号,订单日期)
支付方式(支付宝支付、微信支付)
注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独
计费。
程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信
息报表及货物明细报表。
题目分析:
一、核心业务逻辑分析
1.计费重量规则
核心逻辑:空运计费重量取实际重量与体积重量的较大值。
体积重量计算公式:体积重量(kg)= 长×宽×高(cm³)÷ 6000
示例:若实际重量 80kg,体积 120×80×60cm³,则体积重量为 96kg,计费重量取 96kg。
2.分段费率计算
费率规则(根据计费重量分段):
重量范围(kg) 费率(CNY/kg)
<20 35
20≤重量<50 30
50≤重量<100 25
≥100 15
计算逻辑:基础运费 = 计费重量 × 对应费率。
3.订单与货物关系
一对多关系:一个订单可包含多件货物,每件货物需单独计算计费重量和运费,最终汇总订单总运费。
二、数据结构设计
1.数据实体定义
客户信息:
姓名、电话号码(字符串类型)。
货物信息:
名称、长 / 宽 / 高(数值类型,单位 cm)、实际重量(数值类型,单位 kg)。
运送信息:
发件人(姓名、电话、地址)、收件人(姓名、电话、地址)、航班号(字符串)、订单日期(日期类型)。
航班信息:
航班号、起飞机场城市、降落机场城市、日期、最大载重量(数值类型,单位 kg)。
支付信息:
支付方式(枚举类型:支付宝 / 微信支付)。
2.数据输入与存储
输入方式:通过键盘依次输入文本信息,需注意数据类型校验(如数值合法性、日期格式等)。
存储结构:
订单信息:包含客户、运送、支付信息,以及货物列表(数组 / 列表存储多件货物)。
货物明细:每个货物对象需包含计算后的体积重量、计费重量、运费。
三、功能需求拆解
1.输入模块
输入项:
客户信息:姓名、电话。
货物信息:多件货物依次输入,每件需填写名称、长 × 宽 × 高、实际重量。
运送信息:发件人 / 收件人详情、航班号、订单日期。
支付方式:选择支付宝或微信支付。
校验点:
长 / 宽 / 高必须为正整数,实际重量≥0。
航班号需匹配系统中已存在的航班(若有预设数据)。
订单日期需为有效日期(如不早于当前日期)。
2.计算模块
核心计算步骤:
对每件货物:
计算体积重量 → 比较实际重量,确定计费重量。
根据计费重量查找对应费率,计算单件运费。
汇总所有货物运费,得到订单总运费。
3.报表生成模块
订单信息报表:
展示客户、运送、支付、订单日期、总运费等信息。
货物明细报表:
每件货物的名称、尺寸、实际重量、体积重量、计费重量、费率、运费。
格式要求:使用表格或结构化文本展示,确保可读性(如对齐、单位标注)。
四、边界条件与异常处理
1.特殊场景
单件货物:当货物实际重量等于体积重量时,计费重量取两者中任意值(规则已明确取较大者,相等时无影响)。
多件货物:各件货物独立计算,总运费为单件之和。
重量临界值:
如计费重量为 20kg,适用费率 30 元 /kg(非 35 元 /kg);
计费重量为 100kg,适用费率 15 元 /kg。
2.异常情况
输入非法数据:如长 / 宽 / 高为负数、非数字字符,需提示用户重新输入。
航班不存在:若系统需校验航班号有效性,需预先存储航班数据并匹配。
超重风险:若题目要求校验航班最大载重量,需在订单生成时检查所有货物计费重量总和是否超过航班限制。
我的代码展示:
`import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
// 航班类,负责存储航班相关信息
class Flight {
private String flightNumber;
private String departureCity;
private String arrivalCity;
private LocalDate flightDate;
private double maxLoad;
public Flight(String flightNumber, String departureCity, String arrivalCity, LocalDate flightDate, double maxLoad) {
this.flightNumber = flightNumber;
this.departureCity = departureCity;
this.arrivalCity = arrivalCity;
this.flightDate = flightDate;
this.maxLoad = maxLoad;
}
public String getFlightNumber() {
return flightNumber;
}
public String getDepartureCity() {
return departureCity;
}
public String getArrivalCity() {
return arrivalCity;
}
public LocalDate getFlightDate() {
return flightDate;
}
public double getMaxLoad() {
return maxLoad;
}
}
// 客户类,负责存储客户相关信息
class Customer {
private String customerId;
private String name;
private String phoneNumber;
private String address;
public Customer(String customerId, String name, String phoneNumber, String address) {
this.customerId = customerId;
this.name = name;
this.phoneNumber = phoneNumber;
this.address = address;
}
public String getName() {
return name;
}
public String getPhoneNumber() {
return phoneNumber;
}
}
// 货物类,负责存储货物相关信息并计算计费重量和运费
class Goods {
private String goodsId;
private String name;
private double length;
private double width;
private double height;
private double weight;
public Goods(String goodsId, String name, double length, double width, double height, double weight) {
this.goodsId = goodsId;
this.name = name;
this.length = length;
this.width = width;
this.height = height;
this.weight = weight;
}
public String getName() {
return name;
}
public double getLength() {
return length;
}
public double getWidth() {
return width;
}
public double getHeight() {
return height;
}
public double getWeight() {
return weight;
}
public double getChargeableWeight() {
double volumeWeight = (length * width * height) / 6000;
return Math.max(weight, volumeWeight);
}
public double calculateFreight() {
double chargeableWeight = getChargeableWeight();
if (chargeableWeight < 20) {
return chargeableWeight * 35;
} else if (chargeableWeight < 50) {
return chargeableWeight * 30;
} else if (chargeableWeight < 100) {
return chargeableWeight * 25;
} else {
return chargeableWeight * 15;
}
}
}
// 运送信息类,负责存储运送相关信息
class ShippingInfo {
private String senderName;
private String senderPhone;
private String senderAddress;
private String receiverName;
private String receiverPhone;
private String receiverAddress;
private String flightNumber;
private LocalDate orderDate;
private String orderId;
public ShippingInfo(String orderId, String senderName, String senderPhone, String senderAddress, String receiverName,
String receiverPhone, String receiverAddress, String flightNumber, LocalDate orderDate) {
this.orderId = orderId;
this.senderName = senderName;
this.senderPhone = senderPhone;
this.senderAddress = senderAddress;
this.receiverName = receiverName;
this.receiverPhone = receiverPhone;
this.receiverAddress = receiverAddress;
this.flightNumber = flightNumber;
this.orderDate = 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 String getFlightNumber() {
return flightNumber;
}
public LocalDate getOrderDate() {
return orderDate;
}
public String getOrderId() {
return orderId;
}
}
// 支付方式枚举类,定义支持的支付方式
enum PaymentMethod {
ALIPAY, WECHAT_PAY
}
// 订单类,负责整合订单相关信息
class Order {
private Customer customer;
private List
private ShippingInfo shippingInfo;
private PaymentMethod paymentMethod;
public Order(Customer customer, List<Goods> goodsList, ShippingInfo shippingInfo, PaymentMethod paymentMethod) {
this.customer = customer;
this.goodsList = goodsList;
this.shippingInfo = shippingInfo;
this.paymentMethod = paymentMethod;
}
public Customer getCustomer() {
return customer;
}
public List<Goods> getGoodsList() {
return goodsList;
}
public ShippingInfo getShippingInfo() {
return shippingInfo;
}
public PaymentMethod getPaymentMethod() {
return paymentMethod;
}
public double getTotalChargeableWeight() {
double total = 0;
for (Goods goods : goodsList) {
total += goods.getChargeableWeight();
}
return total;
}
public double getTotalFreight() {
double total = 0;
for (Goods goods : goodsList) {
total += goods.calculateFreight();
}
return total;
}
}
// 主类,用于演示系统的使用
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());
List<Goods> 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 Goods(goodsId, goodsName, goodsLength, goodsWidth, goodsHeight, goodsWeight));
}
// 输入航班信息
String flightNumber = scanner.nextLine();
String departureAirport = scanner.nextLine();
String arrivalAirport = scanner.nextLine();
LocalDate flightDate = LocalDate.parse(scanner.nextLine(), DateTimeFormatter.ISO_DATE);
double maxLoad = Double.parseDouble(scanner.nextLine());
Flight flight = new Flight(flightNumber, departureAirport, arrivalAirport, flightDate, maxLoad);
// 输入订单信息
String orderId = scanner.nextLine();
LocalDate orderDate = LocalDate.parse(scanner.nextLine(), DateTimeFormatter.ISO_DATE);
String senderAddress = scanner.nextLine();
String senderName = scanner.nextLine();
String senderPhone = scanner.nextLine();
String receiverAddress = scanner.nextLine();
String receiverName = scanner.nextLine();
String receiverPhone = scanner.nextLine();
ShippingInfo shippingInfo = new ShippingInfo(orderId, senderName, senderPhone, senderAddress, receiverName,
receiverPhone, receiverAddress, flightNumber, orderDate);
// 选择支付方式
PaymentMethod paymentMethod = PaymentMethod.WECHAT_PAY;
// 创建订单
Order order = new Order(customer, goodsList, shippingInfo, paymentMethod);
// 检查航班载重量
double totalChargeableWeight = order.getTotalChargeableWeight();
if (totalChargeableWeight > flight.getMaxLoad()) {
System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.\n", flightNumber);
return;
}
// 输出订单信息
System.out.printf("客户:%s(%s)订单信息如下:\n", order.getCustomer().getName(), order.getCustomer().getPhoneNumber());
System.out.println("-----------------------------------------");
System.out.printf("航班号:%s\n", order.getShippingInfo().getFlightNumber());
System.out.printf("订单号:%s\n", order.getShippingInfo().getOrderId());
System.out.printf("订单日期:%s\n", order.getShippingInfo().getOrderDate());
System.out.printf("发件人姓名:%s\n", order.getShippingInfo().getSenderName());
System.out.printf("发件人电话:%s\n", order.getShippingInfo().getSenderPhone());
System.out.printf("发件人地址:%s\n", order.getShippingInfo().getSenderAddress());
System.out.printf("收件人姓名:%s\n", order.getShippingInfo().getReceiverName());
System.out.printf("收件人电话:%s\n", order.getShippingInfo().getReceiverPhone());
System.out.printf("收件人地址:%s\n", order.getShippingInfo().getReceiverAddress());
System.out.printf("订单总重量(kg):%.1f\n", totalChargeableWeight);
System.out.printf("微信支付金额:%.1f\n", order.getTotalFreight());
System.out.println("\n货物明细如下:");
System.out.println("-----------------------------------------");
System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
int index = 1;
for (Goods goods : order.getGoodsList()) {
double chargeableWeight = goods.getChargeableWeight();
double freightRate;
if (chargeableWeight < 20) {
freightRate = 35;
} else if (chargeableWeight < 50) {
freightRate = 30;
} else if (chargeableWeight < 100) {
freightRate = 25;
} else {
freightRate = 15;
}
double freight = goods.calculateFreight();
System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n", index++, goods.getName(), chargeableWeight, freightRate, freight);
}
}
}`
对代码的分析:


代码规模相关
行数(Lines):331 行,反映了文件的总体规模。行数较多可能意味着代码逻辑复杂,维护时查找特定功能代码难度增加。
语句数(Statements):195 条,说明实际执行的代码操作数量。语句数与行数的差异可辅助判断代码的紧凑程度,此文件中两者差异较大,可能存在较多空行、注释行等。
代码结构相关
分支语句百分比(Percent Branch Statements):4.1% ,表明代码中分支逻辑(如 if - else、switch 等)占比相对较低,代码的条件判断逻辑不算复杂。
方法调用语句(Method Call Statements):48 ,体现代码中方法调用的频繁程度,一定程度上反映模块间的交互情况。
代码注释相关
含注释行百分比(Percent Lines with Comments):4.5% ,注释占比较低,可能会给代码的可读性和后期维护带来困难,他人理解代码意图成本较高。
面向对象特性相关
类和接口数量(Classes and Interfaces):5 ,有 5 个类或接口,反映了代码的封装层次。
每个类的方法数(Methods per Class):7.00 ,平均每个类有 7 个方法,说明类的功能相对丰富,但也可能存在类职责不够单一的问题。
每个方法的平均语句数(Average Statements per Method):4.14 ,方法内语句数不算多,一定程度上说明方法相对简洁,利于理解和维护。
该代码的缺点:
注释严重不足,这样子很容易导致难以理解代码的逻辑和设计意图,类设计可能存在潜在问题,一些类的职责不清晰,导致修改更加困难,复杂度分布不均,有过长的方法体,嵌套过深的条件或循环结构。
2.题目集09中的"航空货运管理系统(继承与多态):
设计要求:
一、计费重量的确定
空运以实际重量(Gross Weight)和体积重量(Volume Weight)中的较
高者作为计费重量。
计算公式:
体积重量(kg) = 货物体积(长×宽×高,单位:厘米)÷ 6000
示例:
若货物实际重量为 80kg,体积为 120cm×80cm×60cm,则:
体积重量 = (120×80×60) ÷ 6000 = 96kg
计费重量取 96kg(因 96kg > 80kg)。
二、基础运费计算2
费率(Rate):航空公司或货代根据航线、货物类型、市场行情等制定(如
CNY 30/kg)。本次作业费率与货物类型有关,货物类型分为普通货物、危险货
物和加急货物三种,其费率分别为:
计算公式:基础运费 = 计费重量 × 费率 × 折扣率
其中,折扣率是指不同的用户类型针对每个订单的运费可以享受相应的折扣,
在本题中,用户分为个人用户和集团用户,其中个人用户可享受订单运费的 9
折优惠,集团用户可享受订单运费的 8 折优惠。
三、题目说明
本次题目模拟某客户到该航空公司办理一次货运业务的过程:
航空公司提供如下信息:
航班信息(航班号,航班起飞机场,航班降落机场,航班日期,航班最大载
重量)3
客户填写货运订单并进行支付,需要提供如下信息:
客户信息(姓名,电话号码等)
货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)
运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选
航班号,订单日期)
支付方式(支付宝支付、微信支付、现金支付)
注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独
计费。
程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信
息报表及货物明细报表。
四、题目要求
本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开
闭原则以及合成复用原则、依赖倒转原则,除需要在 PTA 平台提交源码外,还
需要在超星平台提交本次作业最终得分源码(首次提交最高分源码)的类图,
评判标准为:
基础得分:PTA 实际得分
设计因素:单一职责原则(20%)、里氏代换原则(20%)、开闭原则(20%)、
合成复用原则(20%)、依赖倒转原则(20%)。
最终得分:基础得分扣减所有违背设计原则分值(违背所有五个原则的设计
最终得分为 0 分)
注:提交源码时务必慎重,一定核查是否符合本次题目考核的四个设计原
则,否则一旦提交得到满分则无法修改。(文中红色字体部分为在上次作业基
础上新增的要求)
提醒:本题需求可扩展的类:用户、支付方式、货物

题目分析:
题目集09在题目集08的基础上进行了扩展,增添了客户类型以及不同的客户类型对应不同的折扣方式,不同的货物类型以及不同的计费方式,需要对题目集中的类图进行改进。一些基本的航班类只存储航班信息,货物重量计算类负责计算货物的体积重量计费重量,订单信息类只负责存储订单信息不变。这里的客户类型有两种,个人客户和企业客户。这里有三种支付方式微信支付,现金支付,支付宝支付。所以需要创建一个支付方式的接口。
我的代码展示:
`import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.Scanner;
// ===== 用户类型系统 =====
interface UserType {
String getTypeName();
double getDiscountRate();
}
class IndividualUserType implements UserType {
@Override
public String getTypeName() {
return "Individual";
}
@Override
public double getDiscountRate() {
return 0.9;
}
}
class CorporateUserType implements UserType {
@Override
public String getTypeName() {
return "Corporate";
}
@Override
public double getDiscountRate() {
return 0.8;
}
}
interface UserTypeFactory {
UserType createUserType(String typeName);
}
class DefaultUserTypeFactory implements UserTypeFactory {
@Override
public UserType createUserType(String typeName) {
String upperCaseType = typeName.toUpperCase();
switch (upperCaseType) {
case "INDIVIDUAL":
return new IndividualUserType();
case "CORPORATE":
return new CorporateUserType();
default:
throw new IllegalArgumentException("未知的用户类型: " + typeName);
}
}
}
// ===== 支付方式系统 =====
interface PaymentMethodType {
String getEnglishName();
String getChineseName();
}
class AlipayMethod implements PaymentMethodType {
@Override
public String getEnglishName() {
return "Alipay";
}
@Override
public String getChineseName() {
return "支付宝";
}
}
class WechatMethod implements PaymentMethodType {
@Override
public String getEnglishName() {
return "Wechat";
}
@Override
public String getChineseName() {
return "微信";
}
}
class CashMethod implements PaymentMethodType {
@Override
public String getEnglishName() {
return "Cash";
}
@Override
public String getChineseName() {
return "现金";
}
}
interface PaymentMethodFactory {
PaymentMethodType createPaymentMethod(String methodName);
}
class DefaultPaymentMethodFactory implements PaymentMethodFactory {
@Override
public PaymentMethodType createPaymentMethod(String methodName) {
String upperCaseMethod = methodName.toUpperCase();
switch (upperCaseMethod) {
case "ALIPAY":
return new AlipayMethod();
case "WECHAT":
return new WechatMethod();
case "CASH":
return new CashMethod();
default:
throw new IllegalArgumentException("未知的支付方式: " + methodName);
}
}
}
// ===== 货物类型系统 =====
interface CargoType {
String getName();
double calculateBaseRate(double chargeableWeight);
}
// 普通货物:重量分段费率
class NormalCargoType implements CargoType {
@Override
public String getName() {
return "NORMAL";
}
@Override
public double calculateBaseRate(double chargeableWeight) {
if (chargeableWeight < 20) {
return 35;
} else if (chargeableWeight < 50) {
return 30.0;
} else if (chargeableWeight < 100) {
return 25.0;
} else {
return 15.0;
}
}
}
// 危险货物:重量分段费率
class DangerousCargoType implements CargoType {
@Override
public String getName() {
return "DANGEROUS";
}
@Override
public double calculateBaseRate(double chargeableWeight) {
if (chargeableWeight < 20) {
return 80.0;
} else if (chargeableWeight < 50) {
return 50.0;
} else if (chargeableWeight < 100) {
return 30.0;
} else {
return 20.0;
}
}
}
// 加急货物:重量分段费率
class ExpediteCargoType implements CargoType {
@Override
public String getName() {
return "EXPEDITE";
}
@Override
public double calculateBaseRate(double chargeableWeight) {
if (chargeableWeight < 20) {
return 60.0;
} else if (chargeableWeight < 50) {
return 50.0;
} else if (chargeableWeight < 100) {
return 40.0;
} else {
return 30.0;
}
}
}
interface CargoTypeFactory {
CargoType createCargoType(String typeName);
}
class DefaultCargoTypeFactory implements CargoTypeFactory {
@Override
public CargoType createCargoType(String typeName) {
String upperCaseType = typeName.toUpperCase();
switch (upperCaseType) {
case "NORMAL":
return new NormalCargoType();
case "DANGEROUS":
return new DangerousCargoType();
case "EXPEDITE":
return new ExpediteCargoType();
default:
throw new IllegalArgumentException("未知的货物类型: " + typeName);
}
}
}
// ===== 核心业务类 =====
class Cargo {
private final int id;
private final String name;
private final double width;
private final double length;
private final double height;
private final double weight;
private final CargoType type;
public Cargo(int id, String name, double width, double length, double height, double weight, CargoType type) {
this.id = id;
this.name = name;
this.width = width;
this.length = length;
this.height = height;
this.weight = weight;
this.type = type;
}
public double getChargeableWeight() {
double volumeWeight = (width * length * height) / 6000;
return Math.max(weight, volumeWeight);
}
public double getBaseRate() {
return type.calculateBaseRate(getChargeableWeight());
}
public double calculateBaseFreight() {
return getChargeableWeight() * getBaseRate();
}
public int getId() { return id; }
public String getName() { return name; }
public CargoType getType() { return type; }
}
class User {
private final UserType userType;
private final String id;
private final String name;
private final String phone;
private final String address;
public User(UserType userType, String id, String name, String phone, String address) {
this.userType = userType;
this.id = id;
this.name = name;
this.phone = phone;
this.address = address;
}
public double getDiscountRate() {
return userType.getDiscountRate();
}
public String getName() { return name; }
public String getPhone() { return phone; }
public UserType getUserType() { return userType; }
}
class PaymentMethod {
private final PaymentMethodType methodType;
public PaymentMethod(PaymentMethodType methodType) {
this.methodType = methodType;
}
public String getMethod() {
return methodType.getChineseName();
}
}
class Flight {
private final String flightNumber;
private double currentLoad;
private final double maxLoadCapacity;
public Flight(String flightNumber, double maxLoadCapacity) {
this.flightNumber = flightNumber;
this.maxLoadCapacity = maxLoadCapacity;
this.currentLoad = 0.0;
}
public boolean isOverloaded(double weight) {
return currentLoad + weight > maxLoadCapacity;
}
public void updateLoad(double weight) {
currentLoad += weight;
}
public String getFlightNumber() { return flightNumber; }
}
class Order {
private final String orderNumber;
private final String orderDate;
private final String senderAddress;
private final String senderName;
private final String senderPhone;
private final String receiverAddress;
private final String receiverName;
private final String receiverPhone;
private final PaymentMethod paymentMethod;
private final ArrayList
private final User user;
private final String flightNumber;
public Order(String orderNumber, String orderDate, String senderAddress, String senderName,
String senderPhone, String receiverAddress, String receiverName,
String receiverPhone, PaymentMethod paymentMethod, User user, String flightNumber) {
this.orderNumber = orderNumber;
this.orderDate = orderDate;
this.senderAddress = senderAddress;
this.senderName = senderName;
this.senderPhone = senderPhone;
this.receiverAddress = receiverAddress;
this.receiverName = receiverName;
this.receiverPhone = receiverPhone;
this.paymentMethod = paymentMethod;
this.user = user;
this.flightNumber = flightNumber;
this.cargoList = new ArrayList<>();
}
public void addCargo(Cargo cargo) {
cargoList.add(cargo);
}
public double getTotalWeight() {
return cargoList.stream().mapToDouble(Cargo::getChargeableWeight).sum();
}
public double getTotalBaseCost() {
return cargoList.stream().mapToDouble(Cargo::calculateBaseFreight).sum();
}
public double getTotalCost() {
return getTotalBaseCost() * user.getDiscountRate();
}
public void printOrderInfo() {
System.out.printf("客户:%s(%s)订单信息如下:\n", user.getName(), user.getPhone());
System.out.println("-----------------------------------------");
System.out.printf("航班号:%s\n", flightNumber);
System.out.printf("订单号:%s\n", orderNumber);
System.out.printf("订单日期:%s\n", orderDate);
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", getTotalWeight());
System.out.printf("%s支付金额:%.1f\n\n", paymentMethod.getMethod(), getTotalCost());
}
public void printCargoDetails() {
System.out.println("货物明细如下:");
System.out.println("-----------------------------------------");
System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
for (int i = 0; i < cargoList.size(); i++) {
Cargo c = cargoList.get(i);
System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n",
i+1, c.getName(), c.getChargeableWeight(), c.getBaseRate(), c.calculateBaseFreight());
}
}
}
// ===== 输入处理 =====
interface InputReader {
User readUserInfo(Scanner scanner, UserTypeFactory userFactory);
ArrayList
Flight readFlightInfo(Scanner scanner);
Order readOrderInfo(Scanner scanner, PaymentMethodFactory paymentFactory, User user, String flightNumber);
}
class DefaultInputReader implements InputReader {
@Override
public User readUserInfo(Scanner scanner, UserTypeFactory userFactory) {
String userTypeStr = scanner.nextLine().trim();
UserType userType = userFactory.createUserType(userTypeStr);
String customerId = scanner.nextLine().trim();
String customerName = scanner.nextLine().trim();
String customerPhone = scanner.nextLine().trim();
String customerAddress = scanner.nextLine().trim();
return new User(userType, customerId, customerName, customerPhone, customerAddress);
}
@Override
public ArrayList<Cargo> readCargoInfo(Scanner scanner, CargoTypeFactory cargoFactory) {
String cargoTypeStr = scanner.nextLine().trim();
CargoType cargoType = cargoFactory.createCargoType(cargoTypeStr);
int cargoNum = Integer.parseInt(scanner.nextLine().trim());
ArrayList<Cargo> cargoList = new ArrayList<>();
for (int i = 0; i < cargoNum; i++) {
int id = Integer.parseInt(scanner.nextLine().trim());
String name = scanner.nextLine().trim();
double width = Double.parseDouble(scanner.nextLine().trim());
double length = Double.parseDouble(scanner.nextLine().trim());
double height = Double.parseDouble(scanner.nextLine().trim());
double weight = Double.parseDouble(scanner.nextLine().trim());
cargoList.add(new Cargo(id, name, width, length, height, weight, cargoType));
}
return cargoList;
}
@Override
public Flight readFlightInfo(Scanner scanner) {
String flightNumber = scanner.nextLine().trim();
String depAirport = scanner.nextLine().trim(); // 原代码有此输入,但未使用,可忽略
String arrAirport = scanner.nextLine().trim(); // 原代码有此输入,但未使用,可忽略
String flightDate = scanner.nextLine().trim(); // 原代码有此输入,但未使用,可忽略
double maxLoad = Double.parseDouble(scanner.nextLine().trim());
return new Flight(flightNumber, maxLoad);
}
@Override
public Order readOrderInfo(Scanner scanner, PaymentMethodFactory paymentFactory, User user, String flightNumber) {
String orderNumber = scanner.nextLine().trim();
String orderDate = scanner.nextLine().trim();
String senderAddr = scanner.nextLine().trim();
String senderName = scanner.nextLine().trim();
String senderPhone = scanner.nextLine().trim();
String receiverAddr = scanner.nextLine().trim();
String receiverName = scanner.nextLine().trim();
String receiverPhone = scanner.nextLine().trim();
String payMethod = scanner.nextLine().trim();
PaymentMethodType paymentType = paymentFactory.createPaymentMethod(payMethod);
PaymentMethod payment = new PaymentMethod(paymentType);
return new Order(orderNumber, orderDate, senderAddr, senderName,
senderPhone, receiverAddr, receiverName, receiverPhone, payment, user, flightNumber);
}
}
// ===== 应用入口 =====
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
UserTypeFactory userFactory = new DefaultUserTypeFactory();
PaymentMethodFactory paymentFactory = new DefaultPaymentMethodFactory();
CargoTypeFactory cargoFactory = new DefaultCargoTypeFactory();
InputReader reader = new DefaultInputReader();
try {
// 读取用户信息
User user = reader.readUserInfo(scanner, userFactory);
// 读取货物信息
ArrayList<Cargo> cargoList = reader.readCargoInfo(scanner, cargoFactory);
// 读取航班信息
Flight flight = reader.readFlightInfo(scanner);
String flightNumber = flight.getFlightNumber();
// 读取订单信息
Order order = reader.readOrderInfo(scanner, paymentFactory, user, flightNumber);
// 添加货物到订单并检查载重
for (Cargo c : cargoList) {
double cw = c.getChargeableWeight();
if (flight.isOverloaded(cw)) {
System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.\n", flightNumber);
return;
}
order.addCargo(c);
flight.updateLoad(cw);
}
// 输出结果
order.printOrderInfo();
order.printCargoDetails();
} catch (InputMismatchException e) {
System.out.println("错误:输入格式不正确");
} catch (IllegalArgumentException e) {
System.out.println("错误:" + e.getMessage());
} finally {
scanner.close();
}
对代码的分析:


代码规模相关
行数(Lines = 335):说明该文件代码量不算极少,有一定规模 ,可能实现了较为丰富的功能,但代码行数较多也意味着潜在的维护成本较高,排查问题时可能需要花费更多时间遍历代码。
语句数(Statements = 103):语句数量相对行数不算高,可能存在一些空行、注释行或者长语句拆分情况。语句是执行具体操作的单元,其数量可辅助评估代码实际执行逻辑的多少。
代码结构相关
类和接口数量(Classes and Interfaces = 2):表明代码中定义了 2 个类或接口,代码在结构上可能是模块化设计,将功能分散到不同类型中,利于代码的组织和复用 。
每个类的方法数(Methods per Class = 10.00):平均每个类有 10 个方法,说明类的功能相对丰富,可能承担了较多职责。但从单一职责原则角度看,过高的方法数可能意味着类的功能不够单一,后期修改可能产生较多影响范围。
每个方法的平均语句数(Average Statements per Method = 4.65):方法内平均语句数不算多,意味着方法可能功能相对简单、粒度较细,这是比较好的代码设计,便于理解、测试和维护。
代码控制流相关
分支语句百分比(Percent Branch Statements = 13.6):分支语句占比 13.6% ,说明代码中存在一定的条件判断逻辑,控制流有一定复杂度。合理的分支语句可以实现灵活的逻辑处理,但占比过高可能使代码逻辑变得复杂难读。
该代码的缺点:
注释严重不足数据:注释行占比仅 2.7%,类职责可能不够单一数据:平均每个类有 10 个方法(Methods per Class = 10.00),控制流复杂度问题数据:分支语句占比 13.6%,结合雷达图可能显示部分方法复杂度高,潜在的深度嵌套问题数据:块状直方图显示深度较高的区域存在语句。
三、踩坑心得
一定不要先写代码,不要想到什么写什么,也不要为了方便而忽略类设计的要求,先要将类图写出来再进行代码的编辑,而且要根据题目的条件进行对类的设计,将每个类都要符合设计原理,先弄清楚要求再进行设计,这样子可以保证设计出我们所需要的类。否则发现问题后重新写一遍代码需要耗费更多的精力和时间影响效率。
对于代码注释的问题,每次的代码都会缺少相应的注释,导致隔一天来需要耗费一些时间去重新对代码进行梳理,浪费许多时间,需要养成写注释的习惯。
在代码的编译中需要保证细心,看清楚题目中给出的条件然后再进行代码的编译,这样子才能够确保减少错误,代码有几次编译错误都是因为不细心,没有看清楚题目要求。
四、改进建议
耐心读题,一定先把题读懂读透,再去书写代码,防止遗漏、读错、忘记而导致返工,浪费时间。
增加代码中的注释,在关键的方法和逻辑处添加注释,解释代码的功能和实现思路。
其中很多类的功能太多,还能拆分出更多类出来简化构造方法,拆分main()方法,输入输出部分也可以单独分成一个类,不要在main里放置太多语句。
类设计的时候要符合单一职责原则、里氏代换原则、开闭原则、合成复用原则、依赖倒转原则,对这几个原则要弄清,实现单继承多接口,便于后续迭代只扩增不修改。
继续补充学习java知识点,让代码的应用更加简便,提高效率,
五、总结
现在我们已经完成了俩次大作业的学习,相较于上一次的大作业,虽然这次作业比上一次的更加简单,我也能够得到分数,很明显的一点再面对这种大作业的时候,我不再是不知道做什么,有了第一次的经验,这次的作业明显比上一次的什么都不知道好很多,也有了明确的规划,知道该如何去操作去完成作业,有很多第一次出现的问题也进行了改正,通过第二次的实验,面向对象设计的思维逻辑提升,通过二次迭代开发,深刻理解了类职责划分与抽象类的重要性。然后就是要保证代码的可读性,可维护性,可扩展性。设计代码时需要考虑的要素。对于类的设计需要仔细,弄清楚题目的要求再开始,不能写一步算一步。这几次的大作业不仅仅要求我们的编译能力,更多的是面向对象设计的思维能力。
建议:
老师讲课非常认真,每道题目也都是原创的,希望老师上课能够多用代码进行演示,让我们能够清楚的理解各种代码的各个实例用途。多一点实例的讲解,感谢老师的辛苦贡献。
浙公网安备 33010602011771号