NCHU_OOP_航空货运 管理系统
0. Index
咦惹,怎么这回只做两次作业就要开始写博客了,难道是时间不够了?🤪倒也不是不好,毕竟要迭代我也想不到再怎么迭代了。
只是,这博客是真的难写呀😭总结是真不知道总结什么,这一次不就是更好的类设计嘛,单一职责原则、里氏代换原则、开闭原则以及合成复用原则、依赖倒转原则。
1. 题干
输入格式
客户编号
客户姓名
客户电话
客户地址
运送货物数量
[
货物编号
货物名称
货物宽度
货物长度
货物高度
货物重量
]
// []内的内容输入次数取决于“运送货物数量”,输入不包含“[]”
航班号
航班起飞机场
航班降落机场
航班日期(格式为YYYY-MM-DD)
航班最大载重量
订单编号
订单日期(格式为YYYY-MM-DD)
发件人地址
发件人姓名
发件人电话
收件人地址
收件人姓名
收件人电话
输出格式
客户:姓名(电话)订单信息如下:
航班号:
订单号:
订单日期:
发件人姓名:
发件人电话:
发件人地址:
收件人姓名:
收件人电话:
收件人地址:
订单总重量(kg):
微信支付金额:货物明细如下:
明细编号 货物名称 计费重量 计费费率 应交运费
1 ...
2 ...
其他信息
计费重量等于MAX(实际质量, 体积重量);体积重量等于 体积 / 6000。
货物费率Rate按照重量分四档:
| 计费重量 | (, 20) | [20, 50) | [50, 100) | [100, ) |
|---|---|---|---|---|
| Rate | 35 | 30 | 25 | 15 |
2. 分析
题干就那样啦,可能看题干要点时间,做PTA和打算法比赛(基础)打多了,其实也有读题技巧🤭,就是直接从输入开始读信息,所以我们看一下输入信息,容易看出来我们需要一个客户类、货物类、航班类、订单类、收件人类、发件人类
人
然后对这些整理一下,客户类(Customer)、收件人类(Recipient)、发件人类(Sender),都可以继承自人类(Person),它们都有姓名、电话、地址,客户类再多一个编号。那么这三个的类图设计就如下所示哟(注意这里采用的是合成复用,而不是继承)👇

航班
对于航班类,除了一些基本属性,还需要有一个判断是否超载的方法,而按照面向对象的设计原则、以及考虑拓展性,应当单独给一个接口用于判断是否合法,这个接口后续也可被拓展用于判断除超载外其他方面是否非法,达到了面向接口编程的效果👇

货物
货物类和航班类类似,除了基本属性,还有一个计算费率的方法,这个可以单独提出一个接口,当计算费率发生改变的时候,只需要改变这个接口的实现👇

订单
最后是订单类,一个订单类里面需要包含客户信息、航班信息、货物列表、收/发件人信息、以及金额信息,订单类还包含一个计算金额的方法和一个展示订单信息的方法😶🌫️。除此之外,订单不能凭空产生,一般会有订单管理器,所以另外写一个订单管理器,订单管理器可以生成一份订单,而生成过程也就是从输入读取的过程
![]() |
![]() |
|---|
3. 迭代
第二周,对第一周的航空货运管理系统进行扩展,第二周的扩展是:
🤵♂️给客户增加了类型,有个体类型和集团类型,
🎲给货物增加的类型,分为普通类、加急类、危险类,不同类型的货物的计费Rate不一样,
💰给支付方式添加了类型,有微信支付、支付宝支付、现金支付。
其他没有变化,那么思考一下应该怎么去拓展这些内容,首先,对于这各种各样的类型,肯定不能写死在类里面,不然万一还要拓展呢🥱,可以单独写enum类型,然后相应的类里面包含一个enum对象:

![]() |
![]() |
![]() |
|---|
有一点是,虽然题干说是货物有类型,但是在本次开发中应当把这个类型分配给订单,把货物类型变为订单类型,因为一批订单要么加急要么危险要么普通,加急的和普通的货物不会出现在同一份订单里面😁😁
此外,对于支付方式,英文是它真正的类型,而输出的时候需要它的中文别名.😒
此外,因为不同类型的订单它的货物计费Rate是不一样的,如果写3×4个if-else会相当的冗余,如果后续还要加就会更加冗余,所以这里应当用数组,把不同类型的计费Rate写到数组中,后面再写一个if-else就够了

4. 代码实现
点击查看代码
package 航空货运管理系统;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
// 客户类型枚举
enum CustomerType {
Individual, // 个人
Corporate // 集团
}
// 商品类型枚举
enum OrderType {
Normal, // 常规
Expedite, // 加急
Dangerous // 危险
}
// 支付类型枚举
enum PayType {
Wechat("微信"),
Alipay("支付宝"),
Cash("现金");
private final String description;
PayType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
// 人类(基类)
class Person {
// 字段区
private String name;
private String phone;
private String address;
// 构造区
public Person() {}
public Person(String name, String phone, String address) {
this.name = name;
this.phone = phone;
this.address = address;
}
// 属性区
public String getName() { return name; }
public String getPhone() { return phone; }
public String getAddress() { return address; }
public void setName(String name) { this.name = name; }
public void setPhone(String phone) { this.phone = phone; }
public void setAddress(String address) { this.address = address; }
// 方法区
}
// 客户类(合成复用人类)
class Customer {
// 字段区
private String id;
private CustomerType type;
private final Person person;
// 构造区
public Customer() {
this.person = new Person();
}
public Customer(String id, CustomerType type, Person person) {
this.id = id;
this.type = type;
this.person = person;
}
// 属性区
public CustomerType getType() { return type; }
public String getName() { return person.getName(); }
public String getPhone() { return person.getPhone(); }
// 方法区
}
// 收件人类(合成复用人类)
class Recipient {
private final Person person;
public Recipient() {
this.person = new Person();
}
public String getName() { return person.getName(); }
public void setName(String name) { person.setName(name); }
public String getPhone() { return person.getPhone(); }
public void setPhone(String phone) { person.setPhone(phone); }
public String getAddress() { return person.getAddress(); }
public void setAddress(String address) { person.setAddress(address); }
}
// 发件人类(合成复用人类)
class Sender {
// 字段区
private final Person person;
// 构造区
public Sender() {
this.person = new Person();
}
// 属性区
public String getName() { return person.getName(); }
public String getPhone() { return person.getPhone(); }
public String getAddress() { return person.getAddress(); }
public void setName(String name) { person.setName(name); }
public void setPhone(String phone) { person.setPhone(phone); }
public void setAddress(String address) { person.setAddress(address); }
// 方法区
}
// 航班类
class Flight {
// 字段区
private String number;
private String begin;
private String end;
private Date date;
private double maxCarryWeight;
// 构造区
public Flight() {}
// 属性区
public String getNumber() { return number; }
public double getMaxCarryWeight() { return maxCarryWeight; }
public void setNumber(String number) { this.number = number; }
public void setBegin(String begin) { this.begin = begin; }
public void setEnd(String end) { this.end = end; }
public void setDate(Date date) { this.date = date; }
public void setMaxCarryWeight(double maxCarryWeight) { this.maxCarryWeight = maxCarryWeight; }
// 方法区
}
// 订单类
class Order {
// 字段区
private String id;
private Customer customer;
private Flight flight;
private Date date;
private Sender sender;
private Recipient recipient;
private OrderType type;
private ArrayList<Product> products;
private PayType payType;
private double totalWeight;
private double totalFee;
// 构造区
public Order() {
this.customer = new Customer();
this.flight = new Flight();
this.sender = new Sender();
this.recipient = new Recipient();
this.products = new ArrayList<>();
}
// 属性区
public String getId() { return id; }
public Date getDate() { return date; }
public ArrayList<Product> getProducts() { return products; }
public void setId(String id) { this.id = id; }
public void setCustomer(Customer customer) { this.customer = customer; }
public void setFlight(Flight flight) { this.flight = flight; }
public void setDate(Date date) { this.date = date; }
public void setSender(Sender sender) { this.sender = sender; }
public void setRecipient(Recipient recipient) { this.recipient = recipient; }
public void setType(OrderType type) { this.type = type; }
public void setProducts(ArrayList<Product> products) { this.products = products; }
public void setPayType(PayType payType) { this.payType = payType; }
public void setTotalWeight(double totalWeight) { this.totalWeight = totalWeight; }
// 方法区
public double Fee() {
for(Product product : this.products) {
this.totalFee += product.Fee(type);
}
switch(customer.getType()) {
case Individual -> this.totalFee *= 0.9;
case Corporate -> this.totalFee *= 0.8;
}
return this.totalFee;
}
public void show() {
System.out.printf("客户:%s(%s)订单信息如下:\n", this.customer.getName(), this.customer.getPhone());
System.out.println("-----------------------------------------");
System.out.println("航班号:" + this.flight.getNumber());
System.out.println("订单号:" + this.getId());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println("订单日期:" + sdf.format(this.getDate()));
System.out.println("发件人姓名:" + this.sender.getName());
System.out.println("发件人电话:" + this.sender.getPhone());
System.out.println("发件人地址:" + this.sender.getAddress());
System.out.println("收件人姓名:" + this.recipient.getName());
System.out.println("收件人电话:" + this.recipient.getPhone());
System.out.println("收件人地址:" + this.recipient.getAddress());
System.out.printf("订单总重量(kg):%.1f%n", this.totalWeight);
System.out.printf("%s支付金额:%.1f%n", this.payType.getDescription(), this.Fee());
System.out.println();
System.out.println("货物明细如下:");
System.out.println("-----------------------------------------");
System.out.println("明细编号\t货物名称\t计费重量\t计费费率\t应交运费");
int i = 1;
for(Product product : this.products) {
System.out.printf("%d\t", i);
System.out.printf("%s\t%.1f\t%.1f\t%.1f%n", product.getName(), product.getWeight(), product.getRate(), product.getFee());
i++;
}
}
}
// 商品类
class Product {
// 字段区
private final String name;
private final double width;
private final double length;
private final double height;
private double weight;
private double fee;
private double rate;
// 构造区
public Product(String id, String name, double length, double width, double height, double weight) {
this.name = name;
this.length = length;
this.width = width;
this.height = height;
this.weight = weight;
}
// 属性区
public String getName() { return name; }
public double getWidth() { return width; }
public double getLength() { return length; }
public double getHeight() { return height; }
public double getWeight() { return weight; }
public double getFee() { return fee; }
public double getRate() { return this.rate; }
public void setWeight(double weight) { this.weight = weight; }
// 方法区
public double Fee(OrderType OrType) {
ArrayList<Double> rates = new ArrayList<>();
switch(OrType) {
case Normal:
rates.addAll(Arrays.asList(35.0, 30.0, 25.0, 15.0));
break;
case Expedite:
rates.addAll(Arrays.asList(60.0, 50.0, 40.0, 30.0));
break;
case Dangerous:
rates.addAll(Arrays.asList(80.0, 50.0, 30.0, 20.0));
}
if(this.weight < 20) {
this.rate = rates.get(0);
}
else if (this.weight >= 20 && this.weight < 50) {
this.rate = rates.get(1);
}
else if (this.weight >= 50 && this.weight < 100) {
this.rate = rates.get(2);
}
else {
this.rate = rates.get(3);
}
this.fee = rate*this.weight;
return this.fee;
}
}
// 输入处理器抽象类
abstract class InputHandler {
// 字段区
protected Scanner scanner;
// 构造区
public InputHandler() {
this.scanner = new Scanner(System.in);
}
// 属性区
// 方法区
public abstract Customer inputCustomer();
public abstract OrderType inputOrderType();
public abstract ArrayList<Product> inputProducts();
public abstract Flight inputFlight();
public abstract String inputOrderId();
public abstract Date inputOrderDate();
public abstract Sender inputSender();
public abstract Recipient inputRecipient();
public abstract PayType inputPayType();
}
// 输入处理器具体实现类
class DefaultInputHandler extends InputHandler {
@Override
public Customer inputCustomer() {
String type = scanner.next();
CustomerType customerType = CustomerType.Individual;
for(CustomerType CusType : CustomerType.values()) {
if(CusType.name().equals(type)) {
customerType = CusType;
break;
}
}
return new Customer(
scanner.next(),
customerType,
new Person(scanner.next(), scanner.next(), scanner.next())
);
}
@Override
public OrderType inputOrderType() {
String type = scanner.next();
for(OrderType OrType : OrderType.values()) {
if(OrType.name().equals(type)) {
return OrType;
}
}
return OrderType.Normal;
}
@Override
public ArrayList<Product> inputProducts() {
ArrayList<Product> products = new ArrayList<>();
int numProducts = scanner.nextInt();
for (int i = 0; i < numProducts; i++) {
Product product = new Product(
scanner.next(), scanner.next(),
scanner.nextDouble(), scanner.nextDouble(), scanner.nextDouble(),
scanner.nextDouble()
);
double volumeWeight = product.getLength() * product.getWidth() * product.getHeight() / 6000.0;
product.setWeight(Math.max(volumeWeight, product.getWeight()));
products.add(product);
}
return products;
}
@Override
public Flight inputFlight() {
Flight flight = new Flight();
flight.setNumber(scanner.next());
flight.setBegin(scanner.next());
flight.setEnd(scanner.next());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
flight.setDate(dateFormat.parse(scanner.next()));
}
catch (ParseException e) {
System.out.println();
}
flight.setMaxCarryWeight(scanner.nextDouble());
return flight;
}
@Override
public String inputOrderId() {
return scanner.next();
}
@Override
public Date inputOrderDate() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
return dateFormat.parse(scanner.next());
} catch (ParseException e) {
System.out.println();
return null;
}
}
@Override
public Sender inputSender() {
Sender sender = new Sender();
sender.setAddress(scanner.next());
sender.setName(scanner.next());
sender.setPhone(scanner.next());
return sender;
}
@Override
public Recipient inputRecipient() {
Recipient recipient = new Recipient();
recipient.setAddress(scanner.next());
recipient.setName(scanner.next());
recipient.setPhone(scanner.next());
return recipient;
}
@Override
public PayType inputPayType() {
String type = scanner.next();
for(PayType payType : PayType.values()) {
if(payType.name().equals(type)) {
return payType;
}
}
return PayType.Alipay;
}
}
// 航班验证器接口
interface FlightValidator {
boolean validateFlight(Flight flight, double totalWeight);
}
// 航班验证器接口实现类
class DefaultFlightValidator implements FlightValidator {
@Override
public boolean validateFlight(Flight flight, double totalWeight) {
return flight.getMaxCarryWeight() >= totalWeight;
}
}
// 订单管理器类
class OrderManager {
// 字段区
private final InputHandler inputHandler;
private final FlightValidator flightValidator;
// 构造区
public OrderManager(InputHandler inputHandler, FlightValidator flightValidator) {
this.inputHandler = inputHandler;
this.flightValidator = flightValidator;
}
// 属性区
// 方法区
public Order createOrder() {
Order order = new Order();
// 输入客户信息
order.setCustomer(inputHandler.inputCustomer());
// 输入订单类型
order.setType(inputHandler.inputOrderType());
// 输入货物列表
order.setProducts(inputHandler.inputProducts());
// 计算总重量
double totalWeight = 0;
for (Product product : order.getProducts()) {
totalWeight += product.getWeight();
}
order.setTotalWeight(totalWeight);
// 输入航班信息
Flight flight = inputHandler.inputFlight();
// 是否超重
if (!flightValidator.validateFlight(flight, totalWeight)) {
System.out.printf("The flight with flight number:%s has exceeded its load capacity and cannot carry the order.", flight.getNumber());
System.exit(0);
}
order.setFlight(flight);
// 输入订单编号
order.setId(inputHandler.inputOrderId());
// 输入订单日期
order.setDate(inputHandler.inputOrderDate());
// 输入寄件人信息
order.setSender(inputHandler.inputSender());
// 输入收件人信息
order.setRecipient(inputHandler.inputRecipient());
// 输入支付方式
order.setPayType(inputHandler.inputPayType());
return order;
}
}
// 主函数
public class Main {
public static void main(String[] args) {
InputHandler inputHandler = new DefaultInputHandler();
FlightValidator flightValidator = new DefaultFlightValidator();
OrderManager orderManager = new OrderManager(inputHandler, flightValidator);
Order order = orderManager.createOrder();
order.show();
}
}
5. 代码质量分析

一言难尽,我只能说这个SourceMonitor有待提高,或者说这次的代码确实不适合用这个工具做代码质量分析😡
下面是最终的代码类图(删去一部分无用代码)👀

这份代码的优点🤩:
- 单一职责原则:
每个类职责明确分离,Person只处理人员基本信息,Customer/Recipient/Sender各自处理特定角色OrderManager,专注订单创建流程,InputHandler只处理输入逻辑
- 开闭原则:
通过enum类型(如OrderType)管理分类,抽象类InputHandler和接口FlightValidator允许扩展新实现(如新增AdvancedInputHandler)而不修改原有代码
- 里氏代换原则:
DefaultInputHandler完全实现InputHandler抽象类的契约,DefaultFlightValidator严格遵循FlightValidator接口定义
- 合成复用原则:
优先使用组合,Customer/Recipient/Sender通过组合复用Person的功能(而非继承),Order组合了多个对象(Customer,Flight,Product等)
- 依赖倒置原则:
高层模块不依赖低层细节,OrderManager依赖抽象的InputHandler和FlightValidator通过构造函数注入具体实现(体现控制反转)
- 接口隔离原则:
接口职责单一,FlightValidator仅包含航班验证方法,没有出现臃肿的"上帝接口"
- 迪米特法则:
Order类通过customer.getName()获取客户名(而非直接访问person.name),Recipient/Sender对外仅暴露必要方法(如getName()),隐藏内部Person对象细节
- 组合优于继承原则:
Customer/Recipient/Sender通过组合复用Person功能,未使用继承实现角色分类
- 工厂方法模式:
InputHandler抽象类定义输入对象的创建接口DefaultInputHandler实现具体创建逻辑
- 关注点分离:
输入处理(InputHandler、业务逻辑(Order)、验证逻辑(FlightValidator))分离
6. 结束啦
可恶的Bolg终于写完了,总结:类设计好难!类设计好难!设计原则好难!设计原则好难!😭






浙公网安备 33010602011771号