面向对象程序设计课程题目集 8-9 总结性 Blog
——————————24201321————黄永铎
一、前言
好烦好烦好烦。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
题目集 8 和题目集 9 围绕 “继承与多态”“容器类操作”“复杂系统设计” 等核心知识点展开,涵盖了类的抽象、接口的应用、数据结构的选择以及系统业务逻辑的实现等多个方面。题目集 8 包含 3 道题目,主要涉及点线面的继承重构、电梯调度系统代码分析以及航空货运管理系统的初步设计;题目集 9 在此基础上新增 3 道题目,重点考察魔方类的多态实现、几何对象容器的增删改查以及航空货运系统的扩展设计。从难度上看,题目集 8 以基础类设计和代码分析为主,难度适中;题目集 9 进一步深化系统复杂度,尤其在航空货运系统中引入客户类型、货物类型和支付方式等多维度业务逻辑,对类的封装性、代码的扩展性提出了更高要求。
二、设计与分析
(一)题目集 8 - 航空货运管理系统(基础版)
代码
点击查看代码
import java.text.DecimalFormat;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;
class Customer {
private String id;
private String name;
private String phone;
private String address;
public Customer(String id, String name, String phone, String address) {
this.id = id;
this.name = name;
this.phone = phone;
this.address = address;
}
public String getDisplayInfo() {
return new StringBuffer().append(name).append("(").append(phone).append(")").toString();
}
}
class Cargo {
private String id;
private String name;
private double width;
private double length;
private double height;
private double weight;
private double chargeWeight;
private double rate;
private double fee;
public Cargo(String id, String name, double width, double length, double height, double weight) {
this.id = id;
this.name = name;
this.width = width;
this.length = length;
this.height = height;
this.weight = weight;
calculateChargeWeight();
calculateRate();
calculateFee();
}
private void calculateChargeWeight() {
double volume = width * length * height;
double volumeWeight = volume / 6000;
chargeWeight = Math.max(weight, volumeWeight);
}
private void calculateRate() {
if (chargeWeight < 20) rate = 35;
else if (chargeWeight < 50) rate = 30;
else if (chargeWeight < 100) rate = 25;
else rate = 15;
}
private void calculateFee() {
fee = chargeWeight * rate;
}
public double getChargeWeight() { return chargeWeight; }
public double getRate() { return rate; }
public double getFee() { return fee; }
public String getDisplayInfo(int sequence) {
DecimalFormat df = new DecimalFormat("#0.0");
return new StringBuffer()
.append(sequence).append("\t")
.append(name).append("\t")
.append(df.format(chargeWeight)).append("\t")
.append(df.format(rate)).append("\t")
.append(df.format(fee))
.toString();
}
}
class Flight {
private String id;
private String departure;
private String arrival;
private String date;
private double maxLoad;
private double currentLoad;
public Flight(String id, String departure, String arrival, String date, double maxLoad) {
this.id = id;
this.departure = departure;
this.arrival = arrival;
this.date = date;
this.maxLoad = maxLoad;
this.currentLoad = 0;
}
public boolean checkLoad(double weight) {
return (currentLoad + weight) <= maxLoad;
}
public void addLoad(double weight) {
currentLoad += weight;
}
public String getId() { return id; }
}
class Order {
private String id;
private String date;
private String senderName;
private String senderPhone;
private String senderAddress;
private String receiverName;
private String receiverPhone;
private String receiverAddress;
private double totalWeight;
private double totalFee;
private DecimalFormat df = new DecimalFormat("#0.0");
public Order(String id, String date, String senderAddress, String senderName,
String senderPhone, String receiverAddress, String receiverName, String receiverPhone) {
this.id = id;
this.date = date;
this.senderAddress = senderAddress;
this.senderName = senderName;
this.senderPhone = senderPhone;
this.receiverAddress = receiverAddress;
this.receiverName = receiverName;
this.receiverPhone = receiverPhone;
}
public void addCargoFee(double weight, double fee) {
totalWeight += weight;
totalFee += fee;
}
public String getDisplayInfo(Customer customer, Flight flight) {
StringBuffer sb = new StringBuffer();
sb.append("客户:").append(customer.getDisplayInfo()).append("订单信息如下:\n")
.append("-----------------------------------------\n")
.append("航班号:").append(flight.getId()).append("\n")
.append("订单号:").append(id).append("\n")
.append("订单日期:").append(date).append("\n")
.append("发件人姓名:").append(senderName).append("\n")
.append("发件人电话:").append(senderPhone).append("\n")
.append("发件人地址:").append(senderAddress).append("\n")
.append("收件人姓名:").append(receiverName).append("\n")
.append("收件人电话:").append(receiverPhone).append("\n")
.append("收件人地址:").append(receiverAddress).append("\n")
.append("订单总重量(kg):").append(df.format(totalWeight)).append("\n")
.append("微信支付金额:").append(df.format(totalFee));
return sb.toString();
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Customer customer = new Customer(sc.nextLine(), sc.nextLine(), sc.nextLine(), sc.nextLine());
int cargoCount = Integer.parseInt(sc.nextLine());
Map<Integer, Cargo> cargos = new LinkedHashMap<>();
for (int i = 0; i < cargoCount; i++) {
String id = sc.nextLine();
String name = sc.nextLine();
double width = Double.parseDouble(sc.nextLine());
double length = Double.parseDouble(sc.nextLine());
double height = Double.parseDouble(sc.nextLine());
double weight = Double.parseDouble(sc.nextLine());
cargos.put(i+1, new Cargo(id, name, width, length, height, weight));
}
Flight flight = new Flight(sc.nextLine(), sc.nextLine(), sc.nextLine(), sc.nextLine(), Double.parseDouble(sc.nextLine()));
Order order = new Order(
sc.nextLine(), sc.nextLine(),
sc.nextLine(), sc.nextLine(), sc.nextLine(),
sc.nextLine(), sc.nextLine(), sc.nextLine()
);
boolean overload = false;
for (Map.Entry<Integer, Cargo> entry : cargos.entrySet()) {
Cargo cargo = entry.getValue();
double weight = cargo.getChargeWeight();
if (!flight.checkLoad(weight)) {
overload = true;
break;
}
flight.addLoad(weight);
order.addCargoFee(weight, cargo.getFee());
}
if (overload) {
System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.", flight.getId());
} else {
System.out.println(order.getDisplayInfo(customer, flight));
System.out.println("\n货物明细如下:\n-----------------------------------------");
System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
for (Map.Entry<Integer, Cargo> entry : cargos.entrySet()) {
System.out.println(entry.getValue().getDisplayInfo(entry.getKey()));
}
}
}
}
参数分析:
核心类与方法分析
- Main 类:主逻辑入口
• 复杂度最高方法:main()
o 复杂度值:7(中等复杂度),包含 30 行代码,39 次方法调用,逻辑集中于输入处理和对象初始化。
o 问题:
o 职责过重:承担输入解析、对象创建、业务逻辑调度等多项职责,违反单一职责原则;
o 代码冗长:143 行的main方法包含多层嵌套(最大块深度 4),可读性差;
o 耦合度高:直接操作Cargo/Flight/Order类的属性,未通过接口或服务层解耦。
o 代码片段示例:
点击查看代码
// main方法中直接处理输入与业务逻辑
Customer customer = new Customer(sc.nextLine(), sc.nextLine(), sc.nextLine(), sc.nextLine());
int cargoCount = Integer.parseInt(sc.nextLine());
for (int i = 0; i < cargoCount; i++) {
Cargo cargo = new Cargo(...);
orders.add(cargo);
}
Flight flight = new Flight(...);
if (!flight.checkLoad(totalWeight)) { /* 直接调用Flight逻辑 */ }
2. Cargo 类:货物计算逻辑
• 关键方法:
o calculateRate():复杂度 5,实现阶梯式费率计算,逻辑如下:
java
if (chargeWeight < 20) rate = 35;
else if (chargeWeight < 50) rate = 30;
else if (chargeWeight < 100) rate = 25;
else rate = 15; // 硬编码费率,难以扩展
点击查看代码
public Flight(String id, String dep, String arr, String date, double maxLoad) {
this.id = id;
this.departure = dep;
this.arrival = arr;
this.date = date;
this.maxLoad = maxLoad; // 简单赋值,复杂度来源可能为参数校验
if (maxLoad <= 0) throw new IllegalArgumentException("Max load must be positive"); // 假设存在校验逻辑
}
点击查看代码
public String getDisplayInfo() {
return "客户:" + customer.getName() + "\n" +
"订单总重量:" + totalWeight + "kg\n" +
"货物明细:" + cargoList.stream().map(Cargo::getDisplayInfo).collect(Collectors.joining("\n"));
}
问题:输出逻辑与业务逻辑耦合,可提取独立的OrderFormatter类。
代码质量指标分析
- 可维护性问题
• 注释覆盖率:7.1%(严重不足),仅Main类有少量注释,Cargo类的费率计算、Flight类的载重量校验等核心逻辑完全无注释,导致代码理解成本高。
o 示例:
点击查看代码
// 无注释,无法快速理解逻辑
double volumeWeight = width * length * height / 6000;
chargeWeight = Math.max(weight, volumeWeight);
建议:
点击查看代码
// 计算体积重量(规则:长×宽×高÷6000,单位:kg)
double volumeWeight = width * length * height / 6000;
// 计费重量取实际重量与体积重量的较大值(物流行业抛重规则)
chargeWeight = Math.max(weight, volumeWeight);
• 类复杂度分布:
o Main类复杂度最高(7),其次是Cargo类(5),Customer和Order类复杂度较低,设计相对简单。
2. 代码规范与命名
• 拼写错误:
o Carao类名错误(应为Cargo),导致类定义和实例化错误;
o getDisplaylnfo()方法名拼写错误(lnfo应为Info),影响代码可读性。
• 方法命名不规范:
o calculate ChargeWeight()(含空格)应为calculateChargeWeight(),违反驼峰命名法。
3. 逻辑风险点
• 输入校验缺失:
o 未校验货物尺寸(宽、长、高)和重量是否为负数,可能导致chargeWeight计算异常;
o 未校验航班最大载重量maxLoad是否为正数,可能引发Flight对象状态无效。
• 异常处理缺失:
o 输入非数字字符时(如用户输入字母代替重量),程序会抛出NumberFormatException,但未使用try-catch捕获,导致程序崩溃。
性能与扩展性分析
- 性能关注点
• 循环效率:
o 在main方法中使用普通for循环遍历货物,虽无性能问题,但可改用增强型for循环提升可读性:
• 数据结构选择:
o 使用ArrayList存储货物列表,适用于频繁的添加操作,符合需求;
o 无大规模数据处理,性能瓶颈不明显。 - 扩展性缺陷
• 费率规则硬编码:
货物费率计算逻辑紧耦合在Cargo类中,新增货物类型(如 “易碎品” 需额外费率)时需修改calculateRate()方法,违反开闭原则。
改进方案:
点击查看代码
// 定义策略接口
public interface RateCalculator {
double calculateRate(double chargeWeight);
}
// 具体策略实现
public class NormalRateCalculator implements RateCalculator { ... }
// Cargo类注入策略
private RateCalculator rateCalculator;
public double getRate() { return rateCalculator.calculateRate(chargeWeight); }
核心类职责:
Customer 类:封装客户基本信息,提供信息展示方法getDisplayInfo(),通过构造方法初始化客户编号、姓名、电话和地址。
Cargo 类:处理货物计费逻辑,通过calculateChargeWeight()计算体积重量与实际重量的最大值作为计费重量,根据计费重量动态确定费率calculateRate(),最终计算运费calculateFee()。
Flight 类:管理航班载重量,通过checkLoad()方法校验订单总重量是否超限,addLoad()方法更新当前载重量。
Order 类:协调客户、航班和货物,通过addCargoFee()累加货物费用,getDisplayInfo()整合订单信息并格式化输出。
2. 关键代码逻辑分析
货物计费逻辑:
点击查看代码
private void calculateChargeWeight() {
double volume = width * length * height;
double volumeWeight = volume / 6000;
chargeWeight = Math.max(weight, volumeWeight); // 取实际重量与体积重量的较大值
}
private void calculateRate() {
if (chargeWeight < 20) rate = 35;
else if (chargeWeight < 50) rate = 30;
else if (chargeWeight < 100) rate = 25;
else rate = 15; // 阶梯式费率设计
}
问题:费率规则硬编码在Cargo类中,若业务规则变更需修改类内部代码,违反开闭原则。
改进方向:将费率规则抽象为策略接口,通过策略模式实现动态切换。
订单与航班交互:
点击查看代码
for (Map.Entry<Integer, Cargo> entry : cargos.entrySet()) {
Cargo cargo = entry.getValue();
double weight = cargo.getChargeWeight();
if (!flight.checkLoad(weight)) { // 逐货物校验载重量
overload = true;
break;
}
flight.addLoad(weight);
order.addCargoFee(weight, cargo.getFee());
}
优点:逐货物校验确保航班载重量精确控制,避免整体超重导致的订单失败。
缺点:循环校验逻辑与订单处理耦合在Main类中,可将校验逻辑封装到Flight或Order类中,提升内聚性。
(二)题目集 9 - 航空货运管理系统(扩展版)
代码:
点击查看代码
import java.util.HashMap;
import java.util.Scanner;
// 客户类
class Customer {
private final String type; // 客户类型
private final String name; // 姓名
private final String phone; // 电话
private final String address; // 地址
public Customer(String type, String name, String phone, String address) {
this.type = type;
this.name = name;
this.phone = phone;
this.address = address;
}
public String getType() { return type; }
public String getName() { return name; }
public String getPhone() { return phone; }
}
// 货物类
class Cargo {
private final int id; // 明细编号
private final String name; // 货物名称
private final double width; // 宽(cm)
private final double length; // 长(cm)
private final double height; // 高(cm)
private final double actualWeight; // 实际重量(kg)
private final String type; // 货物类型
private final double volumeWeight; // 体积重量(kg)
private final double chargeWeight; // 计费重量(kg)
private final double rate; // 费率(CNY/kg)
private final double freight; // 应交运费(未折扣)
public Cargo(int id, String name, double width, double length, double height,
double actualWeight, String type) {
this.id = id;
this.name = name;
this.width = width;
this.length = length;
this.height = height;
this.actualWeight = actualWeight;
this.type = type;
// 计算体积重量(保留1位小数)
volumeWeight = Math.round((width * length * height / 6000) * 10) / 10.0;
chargeWeight = Math.max(actualWeight, volumeWeight);
rate = determineRate(chargeWeight, type);
freight = Math.round(chargeWeight * rate * 10) / 10.0;
}
private double determineRate(double weight, String type) {
switch (type) {
case "Normal": // 普通货物
if (weight < 20) return 35;
else if (weight < 50) return 30;
else if (weight < 100) return 25;
else return 15;
case "Dangerous": // 危险货物
if (weight < 20) return 80;
else if (weight < 50) return 50;
else if (weight < 100) return 30;
else return 20;
case "Expedite": // 加急货物
if (weight < 20) return 60;
else if (weight < 50) return 50;
else if (weight < 100) return 40;
else return 30;
default: return 0;
}
}
public int getId() { return id; }
public double getChargeWeight() { return chargeWeight; }
public double getRate() { return rate; }
public double getFreight() { return freight; }
public String getName() { return name; }
}
// 航班类
class Flight {
private final String flightNumber; // 航班号
private final String departureAirport; // 起飞机场
private final String arrivalAirport; // 降落机场
private final String date; // 日期
private final double maxWeight; // 最大载重量(kg)
public Flight(String flightNumber, String departureAirport,
String arrivalAirport, String date, double maxWeight) {
this.flightNumber = flightNumber;
this.departureAirport = departureAirport;
this.arrivalAirport = arrivalAirport;
this.date = date;
this.maxWeight = maxWeight;
}
public String getFlightNumber() { return flightNumber; }
public double getMaxWeight() { return maxWeight; }
}
// 订单类
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 String paymentMethod; // 支付方式
private final Customer customer; // 客户
private final Flight flight; // 航班
private final HashMap<Integer, Cargo> cargoMap = new HashMap<>(); // 货物清单
public Order(String orderNumber, String orderDate, String senderAddress,
String senderName, String senderPhone, String receiverAddress,
String receiverName, String receiverPhone, String paymentMethod,
Customer customer, Flight flight) {
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.customer = customer;
this.flight = flight;
}
public void addCargo(Cargo cargo) { cargoMap.put(cargo.getId(), cargo); }
public double getTotalWeight() {
return cargoMap.values().stream().mapToDouble(Cargo::getChargeWeight).sum();
}
public double getTotalFreight() {
return cargoMap.values().stream().mapToDouble(Cargo::getFreight).sum();
}
public double getDiscountRate() {
return "Corporate".equals(customer.getType()) ? 0.8 : 0.9;
}
public double getPaymentAmount() {
return Math.round(getTotalFreight() * getDiscountRate() * 10) / 10.0;
}
public String getOrderNumber() { return orderNumber; }
public String getOrderDate() { return orderDate; }
public String getSenderAddress() { return senderAddress; }
public String getSenderName() { return senderName; }
public String getSenderPhone() { return senderPhone; }
public String getReceiverAddress() { return receiverAddress; }
public String getReceiverName() { return receiverName; }
public String getReceiverPhone() { return receiverPhone; }
public String getPaymentMethodCN() {
switch (paymentMethod) {
case "Wechat": return "微信";
case "ALiPay": return "支付宝";
case "Cash": return "现金";
default: return paymentMethod;
}
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取客户信息
String customerType = scanner.next(); // 客户类型
String customerId = scanner.next(); // 客户编号
String customerName = scanner.next(); // 姓名
String customerPhone = scanner.next(); // 电话
scanner.nextLine(); // 消耗前面的换行或空格
String customerAddress = scanner.nextLine().trim(); // 客户地址
// 读取货物信息
String cargoType = scanner.next(); // 货物类型
int cargoCount = scanner.nextInt(); // 货物数量
HashMap<Integer, Cargo> cargoMap = new HashMap<>();
for (int i = 0; i < cargoCount; i++) {
int id = i + 1;
// 读取并忽略输入的货物编号
scanner.nextInt();
String name = scanner.next();
double width = scanner.nextDouble();
double length = scanner.nextDouble();
double height = scanner.nextDouble();
double actualWeight = scanner.nextDouble();
Cargo cargo = new Cargo(id, name, width, length, height, actualWeight, cargoType);
cargoMap.put(id, cargo);
}
// 读取航班信息
String flightNumber = scanner.next(); // 航班号
String depAirport = scanner.next(); // 起飞机场
String arrAirport = scanner.next(); // 降落机场
String flightDate = scanner.next(); // 航班日期
double maxWeight = scanner.nextDouble(); // 最大载重量
// 读取订单信息
String orderNumber = scanner.next(); // 订单号
String orderDate = scanner.next(); // 订单日期
scanner.nextLine(); // 消耗订单日期后的换行
String senderAddress = scanner.nextLine().trim(); // 发件人地址
String senderName = scanner.next(); // 发件人姓名
String senderPhone = scanner.next(); // 发件人电话
scanner.nextLine(); // 消耗发件人电话后的换行
String receiverAddress = scanner.nextLine().trim(); // 收件人地址
String receiverName = scanner.next(); // 收件人姓名
String receiverPhone = scanner.next(); // 收件人电话
String paymentMethod = scanner.next(); // 支付方式
// 创建对象
Customer customer = new Customer(customerType, customerName, customerPhone, customerAddress);
Flight flight = new Flight(flightNumber, depAirport, arrAirport, flightDate, maxWeight);
Order order = new Order(orderNumber, orderDate, senderAddress, senderName, senderPhone,
receiverAddress, receiverName, receiverPhone, paymentMethod, customer, flight);
// 添加货物到订单
cargoMap.values().forEach(order::addCargo);
// 检查航班载重量
double totalWeight = order.getTotalWeight();
if (totalWeight > flight.getMaxWeight()) {
System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.%n",
flight.getFlightNumber());
scanner.close();
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.getOrderNumber());
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%n", order.getPaymentMethodCN(), order.getPaymentAmount());
// 输出货物明细
System.out.println("货物明细如下:");
System.out.println("-----------------------------------------");
System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
cargoMap.values().stream()
.sorted((c1, c2) -> Integer.compare(c1.getId(), c2.getId()))
.forEach(c -> System.out.printf("%d\t%s\t%.1f\t%.1f\t%.1f\n",
c.getId(), c.getName(), c.getChargeWeight(), c.getRate(), c.getFreight()));
scanner.close();
}
}
新增设计:
客户类型:通过继承Customer类创建IndividualCustomer(个人客户)和CorporateCustomer(企业客户),未来可扩展客户等级等属性。
货物类型:定义Cargo抽象类,派生出NormalCargo(普通货物)、ExpediteCargo(加急货物)、DangerousCargo(危险货物),通过多态实现不同类型货物的费率计算。
支付方式:在Order类中增加paymentMethod属性,通过枚举或策略模式处理不同支付方式的手续费计算。
参数分析:
核心类深度分析
- Cargo 类:货物计费逻辑
复杂度最高方法:determineRate()
复杂度值:14(高于阈值 10),包含 4 层分支嵌套,逻辑为阶梯式费率判断:
点击查看代码
if (chargeWeight < 20) {
rate = 35;
} else if (chargeWeight < 50) {
rate = 30;
} else if (chargeWeight < 100) {
rate = 25;
} else {
rate = 15;
}
问题:
硬编码费率规则,违反开闭原则;
分支过多导致可读性下降,维护成本高。
优化建议:
引入策略模式,将费率计算封装为独立策略类(如RateStrategy接口);
使用枚举类定义费率区间,减少分支嵌套。
其他方法:
calculateChargeWeight():复杂度 1,逻辑简单(体积重量与实际重量取最大值),但未校验尺寸 / 重量为负数的情况;
getChargeWeight()等 getter 方法:复杂度 1,符合规范。
2. Order 类:订单处理
复杂度最高方法:getPaymentMethodCN()
复杂度值:2,通过简单条件判断转换支付方式中文名称:
点击查看代码
public String getPaymentMethodCN() {
if ("Wechat".equals(paymentMethod)) {
return "微信";
} else if ("ALiPay".equals(paymentMethod)) {
return "支付宝";
} else {
return "现金";
}
}
点击查看代码
// 原代码未注释,无法快速理解逻辑
double volumeWeight = volume / 6000;
chargeWeight = Math.max(weight, volumeWeight);
建议补充:
// 计算体积重量(规则:体积/6000,单位:kg)
double volumeWeight = volume / 6000;
// 计费重量取实际重量与体积重量的较大值(抛重规则)
chargeWeight = Math.max(weight, volumeWeight);
类复杂度(Class Complexity):
Cargo类复杂度最高(14),主要因费率计算逻辑复杂;
Flight类复杂度最低(1),职责单一,设计合理。
2. 代码规范问题
方法命名:
存在拼写错误:Carao应为Cargo(多处出现,如Carao.determineRate());
命名不规范:aetFreiaht()应为setFreight()(明显笔误)。
参数传递:
Order类构造方法参数过多(10 个参数),导致可读性差,建议使用构建者模式(Builder Pattern)重构:
点击查看代码
// 原构造方法
public Order(String orderNumber, String orderDate, String senderAddress,
String senderName, String senderPhone, String receiverAddress,
String receiverName, String receiverPhone, String paymentMethod,
Customer customer, Flight flight) { ... }
优化后:
点击查看代码
public static class Builder { /* 逐步设置参数 */ }
Order order = new Order.Builder().withOrderNumber(...).withCustomer(...).build();
点击查看代码
// 定义策略接口
public interface CargoRateStrategy {
double calculateRate(double chargeWeight);
}
// 具体策略实现
public class NormalCargoStrategy implements CargoRateStrategy { ... }
// Cargo类依赖策略接口
private CargoRateStrategy rateStrategy;
public double getRate() { return rateStrategy.calculateRate(chargeWeight); }
点击查看代码
abstract class Cargo {
abstract double getRate(); // 抽象费率获取方法
}
class ExpediteCargo extends Cargo {
@Override
public double getRate() {
return 40; // 加急货物固定费率
}
}
优点:将费率计算逻辑封装到具体货物子类中,当新增货物类型时只需创建新子类,无需修改现有代码,符合开闭原则。
问题:当前费率为固定值,实际业务中可能需结合货物重量、运输距离等动态计算,可进一步引入策略接口RateStrategy。
输入处理与异常校验:
点击查看代码
try {
// 读取客户类型
String customerType = scanner.next();
if (!("Individual".equals(customerType) || "Corporate".equals(customerType))) {
throw new IllegalArgumentException("Invalid customer type");
}
// 其他输入校验...
} catch (Exception e) {
System.out.println("Wrong Format");
scanner.close();
return;
}
改进:相比题目集 8,新增输入格式校验(如客户类型、货物类型合法性),使用异常处理机制提升程序健壮性,但对非法输入的提示信息仍不够友好,可提供具体错误原因。
三、踩坑心得
(一)输入处理的 “边界地狱”
问题描述:题目集 8 中未对货物尺寸(宽、长、高)进行非负数校验,导致输入-5 3 4时程序抛出NegativeArraySizeException或计算体积重量为负数。
解决过程:
点击查看代码
// 在Cargo构造方法中增加校验
public Cargo(String id, String name, double width, double length, double height, double weight) {
if (width <= 0 || length <= 0 || height <= 0 || weight <= 0) {
throw new IllegalArgumentException("Dimension and weight must be positive");
}
// ...其他代码
}
教训:输入校验应遵循 “早校验早报错” 原则,在数据源头(构造方法或 setter 方法)进行合法性检查,避免错误数据流入业务逻辑层。
(二)类职责混乱导致的维护困境
问题描述:题目集 8 的Order类同时承担订单信息管理、货物费用计算和输出格式化功能,代码行数超过 300 行,可读性极差。
重构方案:
提取OrderFormatter类:专门负责订单信息的格式化输出,将getDisplayInfo()中的字符串拼接逻辑转移至此。
分离费用计算逻辑:在Order类中注入CargoFeeCalculator接口,通过策略模式实现不同货物类型的费用计算。
点击查看代码
// 重构后Order类依赖费用计算策略
public class Order {
private final CargoFeeCalculator feeCalculator;
public Order(CargoFeeCalculator feeCalculator) {
this.feeCalculator = feeCalculator;
}
public double calculateTotalFee() {
return cargos.stream().mapToDouble(c -> feeCalculator.calculate(c)).sum();
}
效果:类职责单一化后,代码可维护性显著提升,新增计费规则时只需实现新的CargoFeeCalculator接口。
(三)多态与容器结合的 “类型擦除陷阱”
问题描述:题目集 9 中使用ArrayList
解决过程:
确保所有Element子类(Point、Line、Plane)正确重写display()方法,返回类型一致(均为void)。
点击查看代码
// Plane类正确重写display方法
class Plane extends Element {
@Override
public void display() {
System.out.println("The Plane's color is:" + color);
}
}
教训:使用容器存储多态对象时,需严格遵循里氏替换原则,确保子类方法签名与父类完全一致,避免运行时ClassCastException。
四、改进建议
(一)代码结构优化
引入设计模式:
策略模式:将货物计费规则、支付方式处理等可变逻辑封装为策略类,如RateStrategy、PaymentStrategy,实现动态切换。
工厂模式:创建CustomerFactory和CargoFactory,根据输入类型生成具体客户或货物对象,避免Main类中大量if-else判断。
分层架构设计:
数据层:负责数据输入输出(如InputHandler、OutputFormatter)。
业务层:处理核心逻辑(如OrderService、FlightService)。
模型层:封装领域对象(Customer、Cargo、Flight等)。
(二)增强代码健壮性
完善异常处理体系:
定义业务异常类(如OverloadException、InvalidInputException),区分系统异常与业务逻辑异常。
使用try-with-resources确保Scanner等资源正确关闭,避免内存泄漏。
单元测试覆盖:
对Cargo类的计费逻辑、Flight类的载重量校验等核心功能编写单元测试,使用 JUnit 测试框架验证边界情况(如计费重量等于体积重量、航班载重量恰好满载)。
(三)提升代码可读性与可维护性
规范命名与注释:
将elsehasdownRequest等 “火星文” 方法名改为hasDownwardRequest,确保见名知意。
在复杂逻辑处添加注释,如Flight类的载重量校验逻辑:
点击查看代码
// 校验当前货物重量是否超过航班剩余载重量,剩余载重量=最大载重量-已装载重量
public boolean checkLoad(double weight) {
return (maxLoad - currentLoad) >= weight;
}
使用代码分析工具:
通过 SourceMonitor 检查类的复杂度(如Cargo类的方法复杂度应控制在 15 以内),避免出现 “上帝方法”。
使用 PowerDesigner 实时生成类图,确保代码与设计保持一致,便于团队协作。
五、总结
(一)知识与技能提升
面向对象设计能力:通过两次题目集,深入理解了继承、多态、接口的实际应用场景,能够根据业务需求合理设计类层次结构(如航空货运系统的客户与货物类型继承体系)。
复杂系统建模:学会从需求中提取核心实体(客户、货物、航班、订单),分析实体间的关联关系(如订单与货物的聚合关系、客户与订单的关联关系),并通过 UML 类图可视化设计。
问题调试与优化:掌握了通过异常堆栈跟踪、代码分段测试(如单独测试Cargo类的计费逻辑)定位问题的方法,能够针对代码异味(如过长方法、过高类复杂度)进行重构。
(二)待改进方向
设计模式的深度应用:目前仅停留在基础模式(策略、工厂)的简单使用,对结构型模式(装饰器、适配器)和行为型模式(观察者、模板方法)的应用仍不熟练,需通过阅读设计模式经典案例进一步学习。
性能优化意识:在处理大量货物或航班数据时,未考虑数据结构的效率(如使用LinkedList而非ArrayList导致删除操作耗时较高),未来需学习算法与数据结构优化系统性能。
文档与协作能力:代码注释和类设计文档不够完善,在团队开发中可能导致沟通成本增加,需养成编写设计文档和 API 文档的习惯。
六、结语
题目集 8-9 的训练不仅是对面向对象编程知识的综合检验,更是对系统设计思维和问题解决能力的全面提升。从最初的类设计混乱、代码漏洞百出,到逐步通过重构实现高内聚低耦合的系统架构,每一次踩坑与改进都是宝贵的成长经验。未来将继续深耕设计模式与软件架构,努力写出更优雅、更健壮的代码,以应对复杂的实际项目挑战。嘻嘻嘻。