面向对象设计1
一、前言
航空器配载与货运管理系统1
(1)题量情况:代码中一共有六个类(Flight、cargo、loadManifest、sort、isoverload、Main)总的代码行数130多行,代码量不大。一共要实现通过装载顺序输出货物的信息,输出装载的总重量以及该航班的最大载重量,输出配载状态,判断是否达到了超载的情况。
(2)难度:难度不大,关键就是分析好几个类之间的关系即可,总体的难度偏小
(3)知识点:
封装特性:成员变量全部是私有化,不可以外部直接修改属性。
单一职责原则:每一个类只做一件事: Flight、cargo:纯实体类,只存储数据,不写业务逻辑;loadManifest:负责货物添加、重量求和; sort:单独封装排序算法,与业务代码解耦; isoverload:只负责超载判定;Main:只做输入调用与结果输出。
使用动态数组:使用Arraylist保存货物对象,实现对象的批量存储与遍历
排序方式:选择排序的思路是每一轮遍历找出当前未排序区间重量最大的货物,放置到靠前位置,和航空器配载优先装载重货的业务逻辑贴合。相比于冒泡排序需要在遍历过程中频繁交换元素,选择排序每一轮仅执行一次交换,交换次数更少,逻辑清晰,不容易在对象交换环节出错。
航空器配载与货运管理系统2
(1)题量情况:代码中一共有九个类(position、Cargo、CargoCompartment、Flight、isoverload、LoadDispatcher、InputValidator、Overall_status、Main)总的代码行数220多行,代码量不大。
在1的基础上增加了航班货运配载模块,增加了个实现货物装载的输出,输出当前货舱装载情况。
(2)难度:相较于1的难度有所提升,主要是实现的业务双重超载判断;单货舱载重上限+航班全局超载,并且又多使用了一个动态数组用来存储货舱的信息。
(3)知识点:
- 关联关系:货舱与装载的货物是聚合关系:货舱销毁了,但是货物本身还是存在的(聚合关系:是公有的,整体和部分时可以独立存在的);货舱与其位置时组合关系:货舱没有了,其Position也不在存在(组合关系:是私有的,整体和部分是捆绑在一起的)
- 集合框架:增加了货舱的动态数组

- 分支逻辑:多层超载判断,返回状态码,用switch统一输出状态,使用状态及机思想

航空器配载与货运管理系统3
(1)题量情况:代码中一共有十一个类(position、Cargo、CargoCompartment、luggage、passenger、Flight、WeightBalanceCalculator、isoverload、LoadDispatcher、InputValidator、Main)总的代码行数360多行。在2的基础是实现飞机载重平衡配载(中心配平)的业务,一道综合性的面向对象的大题。
(2)难度:属于中等偏上,
- UML类关系变多,Flight 聚合货舱、旅客;CargoCompartment 组合 position、聚合 Cargo,需要理清多层整体 - 部分关系.
- 双重约束:① 货舱局部载重上限;② 飞机重心区间安全约束(不再只看重量,还要看重心位置),两个条件都要满足。
- 边界条件增多。
(3)知识点: - 关联关系:组合关系:passenger内部 new luggage、CargoCompartment构造内部 new position,生命周期绑定;
- 聚合关系:Flight持有List<CargoCompartment>、List<passenger>,货舱、旅客对象可以独立存在,再加入航班;
- 集合框架:与2的基础上增加了乘客信息

- 排序方式:改成使用冒泡排序,不再是选择排序的方式
- 增强鲁棒性要求:在数据录入过程中,要对数据进行合法性验证,如果发现有非法输入,立即停止应用程序运行。

二、设计与分析
航空器配载与货运管理系统1
本程序为航班基础货物配载程序,实现航班信息录入、货物信息录入、货物按重量降序排序、舱单打印、超载判定功能,先确定可以分成几个类别来写,以及每个类别对应的属性和方法有哪些 。
总体的业务(除了Main)可以分成5个类别,分别是:
- Fight(航班类){属性1:航班号,属性2:最大承载重量};方法{获取航班号(getFightNo),获取最大承载重量(getmaxWeight)}
- cargo(货物类){属性1:货物名称,属性2:货物重量};方法{获取货物名称(getCargoname),获取货物重量(getCargoWeight)};
- loadManifest(装载舱单清单){属性1:货物的列表(是一个动态数组,方便后续输出)};方法{增加货物(addcargo),总重量的计算(totalWeight),排序(getCargoList)(注意这里的排序方法只能是选择排序,效率更好)};
- sort(排序类,因为loadManifest里面的方法太多,可以把排序分出来){属性:货物的列表};方法{通过选择排序的方法将对象互换,可以使用到ArrayList的set(位置,要换的对象)};
- isoverload(是否超载){属性:总的重量}{方法:与最大承载重量比较并输出};


英文参数 | 中文释义 |
|---|---|
Project Directory | 项目路径 |
Project Name | 项目名称 |
Checkpoint Name | 快照名称 |
File Name | 文件名 |
Lines | 总行数 |
Statements | 有效语句数 |
Percent Branch Statements | 分支语句占比 |
Method Call Statements | 方法调用次数 |
Percent Lines with Comments | 注释行占比 |
Classes and Interfaces | 类与接口总数 |
Methods per Class | 每个类平均方法数 |
Average Statements per Method | 单个方法平均语句数 |
Line Number of Most Complex Method | 最复杂方法所在行号 |
Name of Most Complex Method | 最复杂方法名称 |
Maximum Complexity | 最大圈复杂度 |
Line Number of Deepest Block | 最深嵌套代码块行号 |
Maximum Block Depth | 最大嵌套深度 |
从SoucrceMonitor分析得到,有效的语句有44条,平均每个类1.20个方法,每个方法平均1.83条语句,平均圈复杂度1.20,整体复杂度很低。全文件复杂度最高的方法是loadManifest.totalWeight(),最大圈复杂度仅为 2,最深代码块深度为 4,不存在超长嵌套、臃肿函数,代码拆分比较清爽。
在UML类关系上,loadManifest和cargo是聚合关系,装载舱单内部持有货物列表,舱单和货物对象生命周期相互独立;sort类、isoverload类和loadManifest、Main 之间属于依赖关系,仅在需要排序、超载校验的时候临时调用;

图2-1类图
解释和心得:
(1)面向对象编程 中,不同功能拆分成不同类,实体类保存数据,工具类完成排序,代码结构清晰,增加注释,方便后期维护
(2)ArrayList和c语言里面的数组访问方式不一样,数组用[下标],集合 读取.get(下标)、修改.set(下标,对象),下标只是数字,不能直接当做对象传入 set 方法,这个点很容易编译报错。
(3)
sortList.set(i,sortList.get(maxindex));//将i位置的对象变成maxindex的
sortList.set(maxindex,temp);
这是动态数组的交换对象,因为cargo里面存在两个属性,但是一半c语言里面交换的方式是不太匹配的,使用的方法是sortList(自己定义的变量).set(想要换到哪个位置,想要换的内容);
(4)方法调用时,形参实参要匹配,有参数的方法调用时必须传入对应类型的数据,否则直接编译失败。写代码时注意检查方法签名,不要写多余的变量名。
(5)整体而言,这个相对较简单,只需要想清楚分为几个类别,每个类别需要干什么事情即可.
航空器配载与货运管理系统2
本程序是带多货舱的航班货物配载调度程序,实现航班、货舱、位置、货物的管理,支持按货物重量降序优先装载、单货舱载重校验、航班整体业载与起飞重量双重超载判断、输入范围校验,
(1)先确定好几个类别,根据题目而言,我们必须要有的类是Position(位置类)、Cargo(货物类)、CargoCompartment(货舱类)、Flight(航班类)、LoadDispatcher(调度类)、InputValidator(校验类)。根据题目的输出格式,我么可以看到最后需要我们进行超载的区分,所以我们可以再建立一个(区分超载类)isoverload,再区分超载后,有不同情况的输出,这时我们也可以建立一个(整体超载输出情况)Overall_status。所以我们总共有8个类别
(2)分析这8个类别的静态属性和动态方法
- Position(位置类){属性:row 行号,column 列号};方法 {getPosName (),获取位置名称,返回位置字符串标识};
- Cargo(货物类){属性:cargoname 货物名称,cargoWeight 货物重量,targetID 目标货舱编号};
方法 {getCargoname (),获取货物名称;
getCargoWeight (),获取货物重量;
getTargetID (),获取目标货舱 ID,指明该货物需要装入哪个货舱};
- CargoCompartment (货舱类){属性:id 货舱编号,maxWeight 货舱最大载重,positions 位置集合,cargos 货舱内装载的货物列表(这个是为了方便后续获得货物的详细信息)};
方法 {addCargo (Cargo cargo),尝试向货舱添加货物,判断加入后是否超载,成功返回 true,失败返回 false;
getCurrentWeight (),遍历货物列表,计算货舱当前已装载总重量;
getId (),获取货舱编号;
getCargos (),获取当前货舱内所有货物的列表;
getMaxWeight (),获取货舱允许的最大载重};
- Flight(航班类){属性:flightNO 航班号,maxpayloadWeight 航班最大业载重量,maxtakeoffweight 航班最大起飞重量,cargoCompartments 航班包含的货舱列表(为了方便后续去获取货舱的详细信息和cargos作用一样)};
方法 {
getFlightNO (),获取航班编号;
getMaxWeight (),获取航班最大业载重量;
add (CargoCompartment c),向航班添加货舱对象;
findcargoCompartment (String id),根据货舱编号查找对应的货舱对象,找不到返回 null;gettotalWeight (),遍历所有货舱,计算航班全部货舱当前装载总重量;
getMaxpayloadWeight (),获取航班最大业载;
getMaxtakeoffweight (),获取航班最大起飞重量;
Cragolist (),获取航班下全部货舱的列表};
- LoadDispatcher(调度类){属性:无};
方法 {
sortCargos (List<Cargo> CargoList),采用选择排序,对货物列表按重量降序排序;FindCargo (List<Cargo> CargoList,String cargoname),根据货物名称在货物列表中查找对应的货物对象,找到返回货物,找不到返回 null}。
- InputValidator(校验类){属性:无};
方法 {
incheckINT (int val,int min,int max),校验整数是否在指定区间内,用于输入合法性检查;incheckFLOAT (Double val,Double min,Double max),校验浮点数是否在指定区间内,用于输入合法性检查};
- isoverload(超载判断类){属性:totalWeight 总装载重量,flight 关联的航班对象};
方法 {isoverload (Flight flight),构造方法,绑定需要判断超载的航班;
condition (),读取航班总重量,和航班最大业载、最大起飞重量对比,返回代表超载情况的状态码};
- Overall_status(整体配载状态类){属性:无};
方法 {status (int choice),接收超载状态码,输出对应的配载提示文本,提示航班是否超载以及超载类型};


英文参数 | 中文释义 |
|---|---|
Project Directory | 项目路径 |
Project Name | 项目名称 |
Checkpoint Name | 快照名称 |
File Name | 文件名 |
Lines | 总行数 |
Statements | 有效语句数 |
Percent Branch Statements | 分支语句占比 |
Method Call Statements | 方法调用次数 |
Percent Lines with Comments | 注释行占比 |
Classes and Interfaces | 类与接口总数 |
Methods per Class | 每个类平均方法数 |
Average Statements per Method | 单个方法平均语句数 |
Line Number of Most Complex Method | 最复杂方法所在行号 |
Name of Most Complex Method | 最复杂方法名称 |
Maximum Complexity | 最大圈复杂度 |
Line Number of Deepest Block | 最深嵌套代码块行号 |
Maximum Block Depth | 最大嵌套深度 |
通过 SourceMonitor 对本货运航班管理代码进行度量分析,项目有效语句共 44 条,平均每个类包含 1.20 个方法,每个方法平均 1.83 条语句,平均圈复杂度 1.20,整体复杂度水平很低。结合度量明细可见,大部分为构造方法与 getter 方法,无复杂分支逻辑;项目内圈复杂度最高的方法为 isoverload.condition (),圈复杂度为 5,其次 CargoCompartment.addCargo ()、Flight.findcargoCompartment () 等方法圈复杂度为 2,最深代码块嵌套深度为 5,仅少量代码存在多层分支判断,不存在超长嵌套、臃肿函数,代码拆分清爽,整体可读性与可维护性较好。
在 UML 类关系上,CargoCompartment 和 position 是组合关系,货舱内部包含货位,货舱销毁时对应的货位也随之销毁;CargoCompartment 和 Cargo 是聚合关系,一个货舱可以包含多件货物,货舱销毁,货物对象可以被分配到其他货舱;Flight 和 CargoCompartment 是聚合关系,航班包含多个货舱;其余为依赖关系,与1的基本差不多,增加了货舱类


图2-2类图
解释和心得:
(1)position 是组合,在java组合和聚集可以用私有和公有来区分,组合就相当于是私有的,这个东西不可以替换,而聚集就公有的是可以被替换的,这个position是组合就是私有,所以只能在CargoCompartment里面被定义
(2)cargos是聚合也是聚集,是公有的,可以被替换,所以是在外部进行声明传参的。
(3) 主要的 核心还是分清楚类别,根据职责分类,将不同的功能拆解到独立类中
(4)本航班货运配载程序核心关键点:
①类的划分:梳理业务实体,抽象出位置、货物、货舱、航班等实体类,以及调度、校验、状态判断等工具类;
②对象关系建模:区分组合、聚集两种关联,在 UML 类图中正确绘制,体现面向对象实体之间的包含关系;
③装载逻辑:先对货物按重量降序排序,再遍历货物,匹配目标货舱 ID,调用货舱addCargo()判断货舱载重是否超限;
④两层超载判断:第一层是单个货舱内部载重校验;第二层是航班整体载重校验,分别对比最大业载、最大起飞重量,输出不同等级告警; ⑤输入校验:借助工具类InputValidator对输入数量、数值范围做合法性校验,过滤非法输入。
航空器配载与货运管理系统3
本程序是带多货舱的航班货物配载调度程序(基于2基础上增加的),实现航班、货舱、位置、货物、旅客、行李、载重平衡、超载校验、输入校验的管理,支持货舱载重校验、航班重心配平计算、输入范围校验,输出航班载重平衡舱单并评估重心是否安全。
(1)先确定好几个类别,根据题目而言,我们要增加3个类别,passenger(旅客类)、luggage(行李类)、WeightBalanceCalculator(载重平衡计算类),但是同时在Flight类、inputValidator类、isoverload类都要进行修改
(2)分析这3个类别的静态属性和动态方法
- passenger(旅客类)
属性:{name・旅客编号,Luggageweight・行李对象;静态属性 standardweight・单人标准旅客重量;}
方法:{getname (),获取旅客编号;
getLuggageweight (),获取旅客总重量(旅客自重 + 行李重量);}
- luggage(行李类)
属性:{luggageweight・行李重量;}
方法:{getLuggageweight (),获取行李重量;}
- WeightBalanceCalculator(载重平衡计算类)
属性:{emptyweight・空机重量,emptyarm・空机重心力臂,passengerarm・旅客舱平均力臂,arm1・前货舱力臂,arm2・后货舱力臂,MAC_leading_edge・平均气动弦前缘位置,MAC_length・平均气动弦长度;}
方法:{generateLoadSheet (Flight flight),计算旅客总重、旅客力矩、货物总重、货物力矩、全机总重、全机总力矩、实际重心、重心百分比,返回计算结果数组; safe (Double CG),判断重心百分比是否在安全区间,安全返回 true,危险返回 false;}
- Flight(航班类):增加了passengers・航班旅客列表;passengerList (),获取航班下全部旅客的列表;
- isoverload(超载判断类):修改了输出的格式以及判断的条件

- LoadDispatcher(调度类):修改了排序的方式改成了冒泡排序。


英文参数 | 中文释义 |
|---|---|
Project Directory | 项目路径 |
Project Name | 项目名称 |
Checkpoint Name | 快照名称 |
File Name | 文件名 |
Lines | 总行数 |
Statements | 有效语句数 |
Percent Branch Statements | 分支语句占比 |
Method Call Statements | 方法调用次数 |
Percent Lines with Comments | 注释行占比 |
Classes and Interfaces | 类与接口总数 |
Methods per Class | 每个类平均方法数 |
Average Statements per Method | 单个方法平均语句数 |
Line Number of Most Complex Method | 最复杂方法所在行号 |
Name of Most Complex Method | 最复杂方法名称 |
Maximum Complexity | 最大圈复杂度 |
Line Number of Deepest Block | 最深嵌套代码块行号 |
Maximum Block Depth | 最大嵌套深度 |
通过 SourceMonitor 对本航班载重平衡配载调度代码进行度量分析,项目有效语句共 44 条,平均每个类包含 1.20 个方法,每个方法平均 1.83 条语句,平均圈复杂度 1.20,整体复杂度水平很低。结合度量明细可见,大部分为构造方法与 getter 方法,无复杂分支逻辑;全项目复杂度最高的方法为WeightBalanceCalculator.generateLoadSheet(),圈复杂度为 5,其次isoverload.condition()、WeightBalanceCalculator.safe() 圈复杂度为4、CargoCompartm
ent.addCargo()、CargoCompartment.getCurrentWeight()、Flight.findcargoCompartment()、Flight.gettotalWeight()等方法圈复杂度为 2,代码最大块深度为 6,仅少量代码存在多层分支判断,不存在超长嵌套、臃肿函数,代码拆分清爽,整体可读性与可维护性较好。
在 UML 类关系上,在2的基础上增加了Luggage和Paseenger之间的组合关系。


解释和心得:
(1)Luggage是组合,它与Passenger是组合关系,原因:如果乘客被摧毁了,那么他携带的行李就不可能存在。
(2)WeightBalanceCalculator与Flight是依赖关系,依赖是在某种场景下,需要借助另外一个类来帮助,WeightBalanceCalculator需要借助Flight得到乘客的重量,以及各个货舱的重量
(4)本航班配载计算程序核心关键点:
①类的划分:梳理业务实体,抽象出位置、货物、货舱、航班等实体类,以及调度、校验、状态判断等工具类;
②对象关系建模:区分组合、聚集、依赖关联关系,在 UML 类图中正确绘制,体现面向对象实体之间的包含关系;
③装载逻辑:每装载货物进去就要判断是否达到该仓的最大载重
④重心的计算:物理公式的转化
⑤输入校验:借助工具类InputValidator对输入数量、数值范围做合法性校验,过滤非法输入,以及要判断是否是负数。
三、采坑心得:
对于1而言,我们要注意,排序必须使用选择排序的方式才可以通过测试,以及里面的关联关系、逻辑关系没有搞清楚,开始采用单个变量存储,无法批量添加多个货物,扩展性很差,对于后面新加的货物没有很好的分配;对于2而言,重要是position的组合关系,以及超载的情况下输出条件要把(超载)给删除了,开始不知道可以在货舱里面建立货物数组,这样的话,就可以直接加入该舱的货物;对于3而言,最终要的就是物理公式,以及前后舱满了的输出情况,整数型的校验,最小值应该是1,而不是0.