面向对象程序设计作业集1~3的总结性Blog

------------恢复内容开始------------

写在前面

OOP作业集,以航空器配载与货运管理系统程序为中心,不断增加新的功能,我学到了很多:

熟悉了Java基本语法;
初步建立起了面向对象的设计思路;
认识到了代码可拓展性的重要;
学会了使用Idea等IDE便利代码编辑。
学会了构建自动化测试工具。

对本人而言,三次的题目集的题量整体适中,难度呈上升趋势。

Complexity Metrics(复杂度分析)

因为下面要用到复杂度分析,所以先在此给出一些相关概念。
我们需要使用的主要是方法和类的复杂度分析。
方法的复杂度分析主要基于循环复杂度的计算。循环复杂度是一种表示程序复杂度的软件度量,由程序流程图中的“基础路径”数量得来。

  • ev(G):即Essentail Complexity,用来表示一个方法的结构化程度,范围在[1,v(G)]​之间,值越大则程序的结构越“病态”,其计算过程和图的“缩点”有关。
  • iv(G):即Design Complexity,用来表示一个方法和他所调用的其他方法的紧密程度,范围也在[1,v(G)]​之间,值越大联系越紧密。
  • v(G):即循环复杂度,可以理解为穷尽程序流程每一条路径所需要的试验次数。
    对于类,有OCavg和WMC两个项目,分别代表类的方法的平均循环复杂度和总循环复杂度。
    下面我将从程序结构,公测、互测以及bug分析几个方面来总结我第一单元的三次作业。

【作业背景】

在航空业中,航班起飞前需要进行严格的“配载”,即计算飞机的旅客、货物、行李的重量分布,计算出飞机的重心位置,以确保飞行安全。这三次作业就是围绕这一真实场景展开的。
V2:在第一次作业的基础上,航空公司要求进一步细化配载管理。飞机不再只有一个货舱,而是分为多个货舱(如前舱、后舱等)。每个货舱有独立的最大载重和固定数量的装载位置(行列网格)。地勤人员需要按照货物重量从高到低的顺序,将每件货物选择放入某个货舱,系统需实时检查该货舱是否超载。同时,系统需要记录航班整体的最大起飞重量和最大业载重量,并判断整体是否超载。
V3:在真实的航空业务中,飞机的装载不仅包含货物,还包含旅客及其随身行李。更关键的是,任何装载都必须经过严格的“载重平衡”计算,得出飞机的重心位置,以确保飞行安全。

OOP作业集01(V1)

【需求说明】

设计一个基础的航班货运配载模块。系统需要记录航班的基本信息(航班号、最大起飞重量、最大业载重量)。地勤人员可以按照货物重量从高到低向该航班添加货物(货物名称、重量)。系统需要实时计算当前已装载的总重量,并判断是否超载。

【设计要求】

类设计必须符合单一职责原则(SRP),参考类图如下所示(仅作参考,可自行加工具类)。
注意:类设计如果违背了SRP,此题不得分。

image

输入格式

第1行:输入航班号flightNo(字符串,例如CA1201)
第2行:输入该航班最大载重重量maxWeight(实型数,例如1200.0)
第3行:输入该航班要装载的货物件数n(整型数,例如10)
从第4行开始,循环输入要装载的货物的名称cargoName(字符串)和cargoWeight重量(实型数)

输出格式

前n行按照装载顺序依次输出货物的信息,格式如下:
货物[名称 重量:重量值kg]
第n+1行输出装载的总重量以及该航班最大载重量,格式如下:
总重量: 重量值kg / 最大载重: 载重值kg
最后一行输出配载状态,如果没有超载,则输出配载状态:正常,如果超载则输出警告:严重超载
注意:输出的数值均保留1位小数。

我提交的源码

import java.util.*;
class Cargo{
    private String cargoName;
    double cargoWeight;
    public Cargo(){}
    public Cargo(String cargoName,double cargoWeight) {
        this.cargoName = cargoName;
        this.cargoWeight = cargoWeight;
    }
    public void setCargoName(String cargoName) {
        this.cargoName = cargoName;
    }
    public void setCargoWeight(double cargoWeight) {
        this.cargoWeight = cargoWeight;
    }
    public double getCargoWeight() {
        return cargoWeight;
    }
    public String getCargoName() {
        return cargoName;
    }
}
class Flight{
    private String flightNo;
    private double maxFlight;
    private double maxWeight;
    private int n;
    public Flight(){}
    public void setFlightNo(String flightNo) {
        this.flightNo = flightNo;
    }
    public void setMaxWeight(double maxWeight) {
        this.maxWeight = maxWeight;
    }
    public void setN(int n) {
        this.n = n;
    }
    public int getN(){
        return this.n;
    }
    public double getMaxWeight(){
        return this.maxWeight;
    }
}
class LoadManifest{
    public ArrayList<Cargo> List = new ArrayList<>();
    public void add(Cargo c) {
        List.add(c);
    }
    public double getTotalWeight(){
        double totalWeight = 0;
        for (Cargo c : List) totalWeight += c.getCargoWeight();
        return totalWeight;
    }
}
class CargoSorter {
    public static void sort(ArrayList<Cargo> List) {
        for(int i=0;i<List.size();i++){
            int maxnum=i;
            for(int j=i+1;j<List.size();j++){
                if (List.get(j).getCargoWeight()>List.get(maxnum).getCargoWeight()) {
                    maxnum=j;
                    
                }
              
            } 
            if(maxnum!=i){
                    Cargo temp = List.get(i);
                    List.set(i, List.get(maxnum));
                    List.set(maxnum, temp);
                }
           // System.out.println(List.get(i).getCargoWeight());
        }
       
      
    }
}
public class Main {
    public static boolean check(double current,double max){
        if(current>=max){
            return false;
        }
        else
            return true;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
      
        LoadManifest manifest = new LoadManifest();
        Flight flight = new Flight();
        flight.setFlightNo(sc.next());
        flight.setMaxWeight(sc.nextDouble());
        int n = sc.nextInt();
        flight.setN(n);
        for(int i = 0;i < n;i++){
            String name = sc.next();
            double weight = sc.nextDouble();
            manifest.add(new Cargo(name,weight));
        }
        CargoSorter.sort(manifest.List);
        for(int i = 0;i < manifest.List.size();i++){
            System.out.printf("货物[%s 重量:%.1fkg]\n", manifest.List.get(i).getCargoName(), manifest.List.get(i).getCargoWeight());
        }
        System.out.printf("总重量: %.1fkg / 最大载重: %.1fkg\n", manifest.getTotalWeight(), flight.getMaxWeight());
        if(check(manifest.getTotalWeight(), flight.getMaxWeight())){
            System.out.println("配载状态:正常");
        }
        else{
            System.out.println("警告:严重超载");
        }
    }
}

代码规模

V1的代码规模如下
image

类图

image

复杂度分析

image
由图可知,V1的所有方法的CogC=0,ev(G)=1、iv(G)=1、v(G)=1,说明代码在可读性,可维护性以及可测试性上都极为良好,更多的问题出在对设计要求的违背。

得分详情

image
实际上我的代码存在缺陷,最后经老师复查只得了60分。

我的反思

我的代码没能做到遵守SRP原则,在类的具体内容设计上也没有遵循题目的要求,例如:

  • Flight 类里没有存货物列表,只存了航班号、最大载重、货物件数。
  • 货物全部存在 LoadManifest 的 List 里,Flight 和 LoadManifest 之间没有关联。
  • CargoSorter 只负责排序,不负责装载逻辑。
    类图中 Flight 通过 LoadManifest 关联货物,而我的 Flight 类里既没有货物列表,也没有 addCargo 方法。我把货物全部存在 Main 里的独立 ArrayList 中,导致 Flight 成了摆设。

OOP作业集02(V2)

【需求说明】

设计一个扩展的航班货运配载模块,实现以下功能:

  • 航班信息管理:记录航班号、最大起飞重量、最大业载重量。

  • 货舱管理:
    每个货舱有唯一标识(如“前舱”)、最大载重、行数、列数(组成位置网格)。
    货舱与其位置是组合关系(CargoCompartment 创建时内部生成 Position 列表)。
    货舱与装载的货物是聚合关系(Cargo 可独立存在)。

  • 货物装载:
    输入所有待装载货物(名称、重量、目标货舱ID)。
    系统先按货物重量从高到低排序,再依次尝试将货物装入指定的货舱。
    如果目标货舱的当前重量 + 该货物重量 ≤ 该货舱最大载重,则装载成功,否则装载失败并给出提示。

  • 输出要求:
    按排序后的顺序输出每件货物的装载结果(成功或失败)。
    输出每个货舱的已装载总重量及状态(是否超载)。
    输出航班整体总重量及与最大起飞重量、最大业载重量的对比,判断整体是否超载。

【设计要求】

必须严格遵循单一职责原则(SRP),不得将多个职责合并到一个类中。违背 SRP 本题不得分。

必须实现以下类(参考提供的类图与代码框架):

Position:表示货舱中的一个位置(行、列),提供 getPosName() 等方法。

Cargo:表示货物,包含 id(货物名称)、weight。

CargoCompartment:货舱类,包含 id、maxWeight、positions(组合)、cargos(聚合),提供 addCargo(Cargo cargo)、getCurrentWeight() 等方法。

Flight:航班类,包含航班号、最大起飞重量、最大业载重量、List。

LoadDispatcher:调度类,负责对货物列表按重量降序排序(sortCargos)、查找货物(FindCargo)。

InputValidator:输入校验类,提供整数、浮点数的范围校验方法。

输入格式

第1行:航班号(字符串)
第2行:最大起飞重量(实型)
第3行:最大业载重量(实型)
第4行:货舱数量 m(整数,1 ≤ m ≤ 5)
接下来 m 行,每行描述一个货舱:货舱ID(字符串) 最大载重(实型) 行数(整数) 列数(整数)
接下来一行:货物件数 n(整数,1 ≤ n ≤ 100)
接下来 n 行,每行描述一件货物:货物名称(字符串) 重量(实型) 目标货舱ID(字符串,必须与上面输入的某个货舱ID一致)。
例如:
CA1201
20000
15000
2
前舱 5000 2 5
后舱 8000 3 5
3
电子产品 850 前舱
水果 500 后舱
体育用品 128 前舱

输出格式

  • 首先输出一行:===== 货物按重量降序排序后装载结果 =====
  • 接下来 n 行,按排序后的顺序输出每件货物的装载信息,同等重量的货物按输入的先后顺序排列,格式如下:
    装载成功:货物[名称 重量:重量值kg] → 装入货舱[货舱ID] 成功
    装载失败:货物[名称 重量:重量值kg] → 装入货舱[货舱ID] 失败 (超载)
  • 然后输出一行空行(可选,便于阅读)
  • 按输入顺序依次输出每个货舱的统计信息,格式:
    货舱[货舱ID] 已装重量: 重量值kg / 最大载重: 载重值kg → 状态: 正常/超载
  • 输出航班整体信息:
    航班总重量: 总重量值kg / 最大起飞重量: 起飞重量值kg / 最大业载: 业载值kg
  • 最后一行输出整体配载状态,可能为:
    整体配载状态:正常
    警告:超过最大起飞重量
    警告:超过最大业载重量
    警告:超过最大起飞重量和最大业载重量
    注:所有重量数值保留1位小数。
    例如:
    ===== 货物按重量降序排序后装载结果 =====
    货物[电子产品 重量:850.0kg] → 装入货舱[前舱] 成功
    货物[水果 重量:500.0kg] → 装入货舱[后舱] 成功
    货物[体育用品 重量:128.0kg] → 装入货舱[前舱] 成功
    货舱[前舱] 已装重量: 978.0kg / 最大载重: 5000.0kg → 状态: 正常
    货舱[后舱] 已装重量: 500.0kg / 最大载重: 8000.0kg → 状态: 正常
    航班总重量: 1478.0kg / 最大起飞重量: 20000.0kg / 最大业载: 15000.0kg
    整体配载状态:正常

我提交的源码

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Flight flight = new Flight();
        if (sc.hasNextLine()) {
            String line = sc.nextLine().trim();
            if (line.isEmpty() && sc.hasNextLine()) line = sc.nextLine().trim();
            flight.setFlightNo(line);
        }
        flight.setMaxTakeoffWeight(sc.nextDouble());
        flight.setMaxPayloadWeight(sc.nextDouble());
        
        int m = sc.nextInt();
        ArrayList<CargoCompartment> cargoCompartments = new ArrayList<>();
        for (int i = 0; i < m; i++) {
            CargoCompartment cc = new CargoCompartment(sc.next(), sc.nextDouble(), sc.nextInt(), sc.nextInt());
            cargoCompartments.add(cc);
            flight.addCompartment(cc);
        }
        
        int n = sc.nextInt();
        ArrayList<Cargo> cargos = Agent.loadCargos(n, sc);
        LoadDispatcher.sortCargos(cargos);
        
        System.out.println("===== 货物按重量降序排序后装载结果 =====");
        for (Cargo c : cargos) {
            CargoCompartment target = null;
            for (CargoCompartment cc : cargoCompartments) {
                if (cc.getId().equals(c.getCargoCompartmentId())) {
                    target = cc;
                    break;
                }
            }
            if (target != null) {
                if (target.getCurrentWeight() + c.getWeight() <= target.getMaxWeight()) {
                    target.addCargo(c);
                    System.out.printf("货物[%s 重量:%.1fkg] → 装入货舱[%s] 成功\n",
                            c.getId(), c.getWeight(), target.getId());
                } else {
                    System.out.printf("货物[%s 重量:%.1fkg] → 装入货舱[%s] 失败\n",
                            c.getId(), c.getWeight(), target.getId());
                }
            }
        }
        
        System.out.println();
        double total = 0;
        for (CargoCompartment cc : cargoCompartments) {
            double cur = cc.getCurrentWeight();
            total += cur;
            System.out.printf("货舱[%s] 已装重量: %.1fkg / 最大载重: %.1fkg → 状态: %s\n",
                    cc.getId(), cur, cc.getMaxWeight(), (cur > cc.getMaxWeight() ? "超载" : "正常"));
        }
        
        System.out.printf("航班总重量: %.1fkg / 最大起飞重量: %.1fkg / 最大业载: %.1fkg\n",
                total, flight.getMaxTakeoffWeight(), flight.getMaxPayloadWeight());
        
        if (total > flight.getMaxTakeoffWeight() && total > flight.getMaxPayloadWeight()) {
            System.out.println("警告:超过最大起飞重量和最大业载重量");
        } else if (total > flight.getMaxTakeoffWeight()) {
            System.out.println("警告:超过最大起飞重量");
        } else if (total > flight.getMaxPayloadWeight()) {
            System.out.println("警告:超过最大业载重量");
        } else {
            System.out.println("整体配载状态:正常");
        }
    }
}

class Agent {
    public static ArrayList<Cargo> loadCargos(int n, Scanner sc) {
        ArrayList<Cargo> list = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            list.add(new Cargo(sc.next(), sc.nextDouble(), sc.next(), i));
        }
        return list;
    }
}

class Position {
    private int r, c;
    public void setPosition(int r, int c) { this.r = r; this.c = c; }
    public String getPosName() { return r + " " + c; }
}

class Cargo {
    private String id, tId;
    private double w;
    private int idx;
    public Cargo(String id, double w, String tId, int idx) {
        this.id = id; this.w = w; this.tId = tId; this.idx = idx;
    }
    public String getId() { return id; }
    public double getWeight() { return w; }
    public String getCargoCompartmentId() { return tId; }
    public int getIndex() { return idx; }
}

class CargoCompartment {
    private String id;
    private double maxW;
    private ArrayList<Position> ps = new ArrayList<>();
    private ArrayList<Cargo> cs = new ArrayList<>();
    public CargoCompartment(String id, double maxW, int r, int c) {
        this.id = id; this.maxW = maxW;
        for (int i = 1; i <= r; i++) {
            for (int j = 1; j <= c; j++) {
                Position p = new Position();
                p.setPosition(i, j);
                ps.add(p);
            }
        }
    }
    public void addCargo(Cargo c) { cs.add(c); }
    public double getCurrentWeight() {
        double s = 0;
        for (Cargo c : cs) s += c.getWeight();
        return s;
    }
    public String getId() { return id; }
    public double getMaxWeight() { return maxW; }
}

class Flight {
    private String fNo;
    private double mTO, mPL;
    public void setFlightNo(String n) { fNo = n; }
    public void setMaxTakeoffWeight(double w) { mTO = w; }
    public void setMaxPayloadWeight(double w) { mPL = w; }
    public double getMaxTakeoffWeight() { return mTO; }
    public double getMaxPayloadWeight() { return mPL; }
    public void addCompartment(CargoCompartment c) {}
}

class LoadDispatcher {
    public static void sortCargos(ArrayList<Cargo> list) {
        int n = list.size();
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < n - 1 - i; j++) {
                Cargo a = list.get(j);
                Cargo b = list.get(j + 1);
                boolean s = false;
                if (a.getWeight() < b.getWeight()) s = true;
                else if (Math.abs(a.getWeight() - b.getWeight()) < 1e-9 && a.getIndex() > b.getIndex()) s = true;
                if (s) {
                    list.set(j, b);
                    list.set(j + 1, a);
                }
            }
        }
    }
    public static void FindCargo() {}
}

class InputValidator {
    public static boolean isCargoCompartmentInRange(int m) { return m >= 1 && m <= 5; }
    public static boolean isCargoInRange(double n) { return n >= 1 && n <= 100; }
}

代码规模

V2的代码规模如下
image

类图

image

复杂度分析

image
可以看出 Main.main () 方法和 LoadDispatcher.sortCargos () 方法的复杂度偏高,因为这两个方法有一个共同点,就是把过多的逻辑和条件判断都集中在了同一个方法里。
而 Main.main () 方法复杂度严重超标,是因为将输入处理、参数校验、业务调度、结果输出等所有程序流程都堆砌在了主方法中,没有进行合理的职责拆分。LoadDispatcher.sortCargos () 方法复杂度接近阈值,是因为在同一个方法里同时处理了多种排序规则和边界情况判断,没有将不同的排序逻辑抽离成独立的子方法。
因此V2在设计和功能实现上是优于V1的,但是复杂度过高,这一点不如V1(当然也是因为V1的代码机构明显比V2要简单得多,复杂度自然没那么高)。

得分详情

image

我吸取了V1的教训,充分了解了题目的需求和设计要求,再加上通过合理使用Idea编译器,代码的耦合性相比V1有了明显的降低,因此最终取得了100分。

我的反思

V2我重点关注了封装性和SRP原则的执行,尽可能地给代码解耦,也利用了idea这种IDE来便利我的代码编写,减少了很多语法错误并满足了题目的所有要求,但是又暴露出了新的问题,那就是,我的代码的复杂度有些高,可读性比较差,我知道这对于将来的团队开发是很要不得的,因此应当及时改正。

OOP作业集03(V3)

【需求说明】

  • 系统预设常量(需在计算类中定义为静态常量)
    基准数据:空机重量 40,000.0 kg,空机重心力臂 16.25 m。
    力臂参数:
    旅客舱平均力臂:18.0 m
    前货舱力臂:12.0 m (根据货舱ID值为1判断)
    后货舱力臂:22.0 m (根据货舱ID值为2判断)
    MAC参数:MAC长度 5.0 m,MAC前缘距基准距离 15.0 m。
    安全范围:CG% 在 25.0% 到 38.0% 之间为安全。
  • 计算步骤与公式
    第一步:计算旅客总重量及力矩
    旅客总重量 = Σ (单名旅客总重) (注:单名旅客总重 = 75.0kg + 其行李重量)
    旅客总力矩 = 旅客总重量 × 旅客舱力臂
    第二步:计算各货舱总重量及力矩
    遍历航班的所有 CargoCompartment,累加每个舱的当前货物重量。
    货舱总力矩 = Σ (某货舱当前重量 × 该货舱对应力臂)
    第三步:计算全机总重量与总力矩
    全机总重量 = 空机重量 + 旅客总重量 + 所有货舱总重量
    全机总力矩 = (空机重量 × 空机力臂) + 旅客总力矩 + 货舱总力矩
    第四步:计算实际重心
    实际重心 = 全机总力矩 / 全机总重量
    第五步:换算重心百分比 (CG% MAC) 并评估
    CG% MAC = ( (实际重心 - MAC前缘距离) / MAC长度 ) × 100%
    判断:如果 25.0 <= CG% MAC <= 38.0,评估结果为“安全(GREEN)”,否则为“危险(RED - 超出极限重心范围)”。
  • 本次迭代(V3)在V2基础上的主要增量
    引入旅客实体:新增旅客及行李的管理,旅客体重按标准值(75kg)自动计算。
    引入核心业务算法:新增基于物理力矩的“航空器载重平衡计算”功能。
    增强鲁棒性要求:在数据录入过程中,要对数据进行合法性验证,如果发现有非法输入,立即停止应用程序运行。

【设计要求】

本次迭代新增约4个类,系统总类数达到10个左右。严禁使用继承和多态(无extends、无interface、无instanceof),请严格遵循以下关系与单一职责设计(违反设计要求则扣分):
image
注:原有的 Flight 类需增加List 属性,体现关联关系。原有的 LoadDispatcher(排序)在本次生成报告时需被内部调用。另外,本次作业中的排序算法不允许使用朗姆达表达式及Collections.sort()方法,必须使用循环完成排序过程(算法采用冒泡排序)

输入格式

第1行,航班号(字符串)
第2行,前舱货架行号(整型,≥0)
第3行,前舱货架列号(整型,≥0)
第4行,前舱最大载重量(实型,≥0.0)
第5行,后舱货架行号(整型,≥0)
第6行,后舱货架列号(整型,≥0)
第7行,后舱最大载重量(实型,≥0.0)
第8行,乘客人数n(整型,≥0)
从第9行开始,连续n行,输入每名乘客所携带行李重量(实型数,≥0.0)
货物总件数m(整型,≥0)
装载在前舱的货物件数p(整型,0≤p≤m)
接下来连续输入装载在前舱的p件货物的信息,分别为:
货物编号(整型,≥0)
货物重量(实型,≥0.0)
在输入装载在后舱的m-p件货物的信息,分别为:
货物编号(整型,≥0)
货物重量(实型,≥0.0)

输出格式

image

我提交的源码

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String flightNo = sc.nextLine();
        Flight flight = new Flight(flightNo);
        int frontRows = InputValidator.readInt(sc);
        int frontCols = InputValidator.readInt(sc);
        double frontMaxWeight = InputValidator.readDouble(sc);
        CargoCompartment frontComp = new CargoCompartment("1",frontMaxWeight,frontRows,frontCols);
        flight.addCompartment(frontComp);
        int rearRows = InputValidator.readInt(sc);
        int rearCols = InputValidator.readInt(sc);
        double rearMaxWeight = InputValidator.readDouble(sc);
        CargoCompartment rearComp = new CargoCompartment("2",rearMaxWeight,rearRows,rearCols);
        flight.addCompartment(rearComp);
        int passengerCount = InputValidator.readInt(sc);
        for (int i = 0; i < passengerCount; i++) {
            double LuggageWeight = InputValidator.readDouble(sc);
            Passenger p = new Passenger(LuggageWeight);
            flight.addPassenger(p);
        }
        int cargoCount = InputValidator.readInt(sc);
        int frontCargoCount = InputValidator.readScope(sc,1,cargoCount);
        int rearCargoCount = cargoCount - frontCargoCount;
        for (int i = 0; i < frontCargoCount; i++) {
            int cargoId = InputValidator.readInt(sc);
            String CargoId =String.valueOf(cargoId);
            double CargoWeight = InputValidator.readDouble(sc);
            double neweight = CargoWeight + frontComp.getTotalWeight();
            if(neweight > frontComp.getMaxWeight()) {
                System.out.printf("!!! 警告:[1]剩余容量不足(当前载重%.1f/%.1fkg),请重新分配或减轻重量!\n", frontComp.getTotalWeight(), frontComp.getMaxWeight());
                System.exit(0);
            }
            Cargo c = new Cargo(CargoId ,CargoWeight);
            frontComp.addCargo(c);
        }
        for (int i = 0; i < rearCargoCount; i++) {
            int cargoId = InputValidator.readInt(sc);
            String CargoId =String.valueOf(cargoId);
            double CargoWeight = InputValidator.readDouble(sc);
            double neweight = CargoWeight + rearComp.getTotalWeight();
            if(neweight > rearComp.getMaxWeight()) {
                System.out.printf("!!! 警告:[2]剩余容量不足(当前载重%.1f/%.1fkg),请重新分配或减轻重量!\n", rearComp.getTotalWeight(),rearComp.getMaxWeight());
                System.exit(0);
            }
            Cargo c = new Cargo(CargoId ,CargoWeight);
            rearComp.addCargo(c);
        }
        WeightBalanceCalculator.generateLoadSheet(flight);
    }
}

class Passenger{
    Luggage luggage;

    public Passenger(double luggageWeight){
        this.luggage = new Luggage(luggageWeight);
    }
    public double getTotalWeight(){
        return 75.0 + luggage.getWeight();
    }
}

class Luggage{
    private double weight = 0;

    public Luggage(double weight) {
        this.weight = weight;
    }
    public double getWeight() {
        return weight;
    }
}


class WeightBalanceCalculator {
    public static final double EMPTY_WEIGHT = 40000.0;
    public static final double EMPTY_CG_ARM = 16.25;
    public static final double PASSENGER_ARM = 18.0;
    public static final double FWD_CARGO_ARM = 12.0;
    public static final double AFT_CARGO_ARM = 22.0;
    public static final double MAC_LENGTH = 5.0;
    public static final double MAC_LE_OFFSET = 15.0;
    public static final double CG_SAFE_MIN = 25.0;
    public static final double CG_SAFE_MAX = 38.0;

    public static void generateLoadSheet(Flight flight) {
        double passengerWeight = 0;
        for (Passenger p : flight.getPassengers()) {
            passengerWeight += p.getTotalWeight();
        }
        double passengerMoment = passengerWeight * PASSENGER_ARM;

        CargoCompartment front = null, rear = null;
        for (CargoCompartment cc : flight.getCompartments()) {
            if (cc.getId().equals("1")) front = cc;
            else if (cc.getId().equals("2")) rear = cc;
        }

        double frontWeight = front.getTotalWeight();
        double rearWeight = rear.getTotalWeight();
        double frontMoment = frontWeight * FWD_CARGO_ARM;
        double rearMoment = rearWeight * AFT_CARGO_ARM;
        double cargoTotalWeight = frontWeight + rearWeight;
        double cargoTotalMoment = frontMoment + rearMoment;

        double totalWeight = EMPTY_WEIGHT + passengerWeight + cargoTotalWeight;
        double totalMoment = EMPTY_WEIGHT * EMPTY_CG_ARM + passengerMoment + cargoTotalMoment;
        double cg = totalMoment / totalWeight;
        double cgPercent = ((cg - MAC_LE_OFFSET) / MAC_LENGTH) * 100.0;
        boolean isSafe = cgPercent >= CG_SAFE_MIN && cgPercent <= CG_SAFE_MAX;

        System.out.println("======================================");
        System.out.println("     航班 " + flight.getFlightNo() + " 载重平衡舱单");
        System.out.println("======================================");
        System.out.println("【基础数据】");
        System.out.printf("空机重量: %.1f kg  | 力臂: %.1f m\n", EMPTY_WEIGHT, EMPTY_CG_ARM);
        System.out.println("【旅客数据】");
        System.out.printf("旅客人数: %d  | 总重(含行李): %.1f kg\n", flight.getPassengers().size(), passengerWeight);
        System.out.println("【货物数据】");
        System.out.printf("[1] 装载: %.1fkg / %.1fkg (力臂: %.1fm)\n", frontWeight, front.getMaxWeight(), FWD_CARGO_ARM);
        for (Cargo c : front.getCargos()) {
            System.out.printf("  ->[%s, %.1fkg]\n", c.getId(), c.getWeight());
        }
        System.out.printf("[2] 装载: %.1fkg / %.1fkg (力臂: %.1fm)\n", rearWeight, rear.getMaxWeight(), AFT_CARGO_ARM);
        for (Cargo c : rear.getCargos()) {
            System.out.printf("  ->[%s, %.1fkg]\n", c.getId(), c.getWeight());
        }
        System.out.println("【汇总计算】");
        System.out.printf("起飞总重量: %.1f kg\n", totalWeight);
        System.out.printf("总力矩:%.1f kg·m\n", totalMoment);
        System.out.printf("实际重心(CG): %.1f m\n", cg);
        System.out.printf("重心百分比(%%MAC): %.1f%%\n", cgPercent);
        System.out.println("======================================");
        if (isSafe) {
            System.out.println("配平评估: 安全 (GREEN)");
        } else {
            System.out.println("配平评估: 危险 (RED - 超出极限重心范围)");
        }
        System.out.println("======================================");
    }
}

class Position {
    private int r, c;
    public void setPosition(int r, int c) { this.r = r; this.c = c; }
    public String getPosName() { return r + " " + c; }
}

class Cargo {
    private String id;
    private double weight;
    public Cargo(String id, double w) {
        this.id = id; this.weight = w;
    }
    public String getId() { return id; }
    public double getWeight() { return weight; }
}

class CargoCompartment {
    private String id;
    private double maxWeight;
    private ArrayList<Cargo> cargos = new ArrayList<>();
    private ArrayList<Position> positions = new ArrayList<>();

    public CargoCompartment(String id, double maxW, int r, int c) {
        this.id = id; this.maxWeight = maxW;
        for (int i = 1; i <= r; i++) {
            for (int j = 1; j <= c; j++) {
                Position p = new Position();
                p.setPosition(i, j);
                positions.add(p);
            }
        }
    }
    public void addCargo(Cargo c) { cargos.add(c); }
    public double getCurrentWeight() {
        double s = 0;
        for (Cargo c : cargos) s += c.getWeight();
        return s;
    }
    public String getId() { return id; }
    public double getMaxWeight() { return maxWeight; }
    public double getTotalWeight() {
        double s = 0;
        for (Cargo c : cargos) s += c.getWeight();
        return s;
    }
    public ArrayList<Cargo> getCargos() { return cargos; }
}

class Flight {
    private String flightNo;
    private ArrayList<CargoCompartment> compartments;
    private ArrayList<Passenger> passengers =  new ArrayList<>();
    public Flight(String flightNo) {
        this.flightNo = flightNo;
        this.compartments = new ArrayList<>();
        this.passengers = new ArrayList<>();
    }
    public void addCompartment(CargoCompartment cc) {
        compartments.add(cc);
    }
    public void addPassenger(Passenger p) {
        passengers.add(p);
    }
    public String getFlightNo() { return flightNo; }
    public ArrayList<CargoCompartment> getCompartments() { return compartments; }
    public ArrayList<Passenger> getPassengers() { return passengers; }
}

class LoadDispatcher {
    public static void sortCargos(ArrayList<Cargo> list) {
        int n = list.size();
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < n - 1 - i; j++) {
                if (list.get(j).getWeight() < list.get(j + 1).getWeight()) {
                    Cargo temp = list.get(j);
                    list.set(j, list.get(j + 1));
                    list.set(j + 1, temp);
                }
            }
        }
    }

    public static void FindCargo() {

    }
}

class InputValidator {
    private Scanner sc;

    public static int readInt(Scanner sc) {
        int val = sc.nextInt();
        if (val < 0) {
            System.out.println("数值不能为负数!");
            System.exit(0);
        }
        return val;
    }
    public static double readDouble(Scanner sc) {
        double val = sc.nextDouble();
        if (val < 0) {
            System.out.println("数值不能为负数!");
            System.exit(0);
        }
        return val;
    }
    public static int readScope(Scanner sc, int min, int max) {
        int val = sc.nextInt();
        if (val < min || val > max) {
            System.out.println("输入必须在 " + min + " 到 " + max + " 之间!");
            System.exit(0);
        }
        return val;
    }
}

代码规模

V3的代码规模如下
image

类图

image

复杂度分析

image
与V2相比整体质量提升显著,平均圈复杂度从 2.12 降至 1.83、平均认知复杂度从 1.79 降至 1.10,此前最严重的两个问题点均已彻底解决了:Main.main 方法认知复杂度从 25 降至 7、圈复杂度从 16 降至 6,LoadDispatcher.sortCargos 方法认知复杂度从 11 降至 6、圈复杂度从 7 降至 4,都回到了一个比较合适的范围。目前来讲代码整体不错,绝大多数方法结构简洁职责清晰,只有WeightBalanceCalculator 核心计算方法因耦合了乘客统计、货舱核算、平衡校验等多个步骤,认知复杂度 10、圈复杂度 9 接近阈值,另有两个方法存在轻微职责重叠问题。

得分详情

PTA上已得到100分,得老师复查确定最终成绩。

总结

从这三次习题集中我的代码表现可以看出,我渐渐熟练掌握了IDE的使用,也意识到了需求分析和设计分析对于面向对象的重要意义。并且在撰写本篇blog的过程中,我也接触并了解了代码规模和复杂度的概念,学会了用工具去检查代码的规模和复杂度,学会了用SRP,耦合性,规模和复杂度去衡量一份代码的品质,我认为这对我是弥足珍贵的。

posted @ 2026-05-16 17:22  负重静好  阅读(20)  评论(0)    收藏  举报