题目集8-9总结blog

一、 前言
题目集8一共3个题,前俩题都是在前面题集的基础上进行拓展,分别拓展了多态,继承和抽象类,继承,多态,接口。不断的进行抽象化,让代码变得容易实现,耦合度变低,尽可能的实现了单一职责、封装和多态的拓展性。虽然复杂度变高难度变大,但是代码更容易维护和修改。第三个题是航空货运管理系统,这个题设计的知识点很多,涉及到封装,抽象类,继承,接口,多态等等还要实现单一职责原则、里氏代换原则、开 闭原则以及合成复用原则。难度很大,但是根据题目的思路,按步骤设计,其实代码的逻辑性很强,也很容易理解,就是类太多容易搞乱。
题目9一共3个题,第一个题是魔方问题,通过设计2个单位立方体的类,然后通过阶层来求他们的体积和表面积中间也运用了继承和依赖关系,确保单一职责。第二个题在第八题目集点和线的基础上再次拓展了面对象,并对该容器进行相应增、删、遍历操作。这个知识点运用了arraylist的知识点和继承多态,比之前的题目难了一点,但是搞清他的具体类的作用还是很容易入手的。第三题是航空货运管理系统的升级版,我感觉这个题目比之前的难了很多,首先在计算费率那里我拓展了
3个货物子类的费率然后客户也分了2个类来计算折扣,支付方式也添加了现金类。类跟类的耦合度很紧密,但尽可能的降低他们的耦合度,保持5个原则:单一职责原则、里氏代换原则、开 闭原则以及合成复用原则、依赖倒转原则。难度确实很大,因为测试点对了但是有时候就是报错,计算重量的地方最容易出错。
二、 设计与分析
题目集八:点线面问题重构和雨刷程序重构
1.在“点与线(类设计)”题目基础上,对题目的类设计进行重构,以实现继承与多态的技术性需求。
对题目中的点Point类和线Line类进行进一步抽象,定义一个两个类的共同父类Element(抽象类),将display()方法在该方法中进行声明(抽象方法),将Point类和Line类作为该类的子类。
再定义一个Element类的子类面Plane,该类只有一个私有属性颜色color,除了构造方法和属性的getter、setter方法外,display()方法用于输出面的颜色,输出格式如下:The Plane's color is:颜色
在主方法内,定义两个Point(线段的起点和终点)对象、一个Line对象和一个Plane对象,依次从键盘输入两个Point对象的起点、终点坐标和颜色值(Line对象和Plane对象颜色相同),然后定义一个Element类的引用,分别使用该引用调用以上四个对象的display()方法,从而实现多态特性。
2. 虽然以上的程序基本能够对上一节提出的雨刷系统进行仿真模拟,但在实际生活中,从细分市场的 角度来讲,一个生产雨刷系统的厂家不可能只有单一的一种雨刷产品,例如,下面是一种更加“高 级”的雨刷系统需求: 表2 “高级”手动雨刷系统控制速度表 控制杆档位 停止 间歇 间歇 间歇 间歇 间歇 低速 高速 超高速 刻度盘刻度 — 1 2 3 4 5 — — — 雨刷速度/分钟 0 4 6 12 15 20 30 60 90 即控制杆多出来一个“超高速”档位,其雨刷摆动速度为90;刻度盘多出两个刻度,刻度4时 雨刷摆动速度为15,刻度5时雨刷摆动速度为20。 而此时,附件一提供的程序就无法满足此种雨刷系统的功能需求,因此就必须进行相应的修 改,但从企业的角度来讲,两种雨刷系统完全可以并存,而是希望程序能够根据雨刷系统的类型自 动匹配其业务逻辑,也就是说,我们的程序不仅要能够适应第一种雨刷系统的需求,也能够适应第 二种(可以有很多种)雨刷系统的需求,且雨刷系统的种类可以随着产品的更新换代进行“插拔 式”增减,此时,必须对程序进行重构,才能达成如上的系统需求。
具体思路:
1.定义基类 Element,包含抽象方法 display (),子类 Point(点)、Line(线)、Plane(平面)分别重写该方法实现各自显示逻辑。程序从控制台读取两个点的坐标(x1,y1/x2,y2)和颜色,验证坐标是否在 1-200 范围内,合法的话创建点、线、平面对象,通过多态调用 display () 输出坐标、颜色、线段长度等信息。
2.定义了雨刷系统的核心部分,控制杆(Lever)、旋钮(Dial)、雨刷(Wiper)和刷子(Brush),通过抽象类和接口规范行为,用两个子类(Wiper1 和 Wiper2)实现两种不同系统的逻辑。用户先选系统类型,再输入操作指令(1-4 控制杆或旋钮升降,0 结束),系统根据指令调整组件状态,计算刷子速度并输出状态信息(如档位、旋钮位置、速度)。
不足和改进:
在点线面中,display代码重复太多了,导致代码冗余严重,可以提取到一个公共类中减少代码冗余。代码只验证了用户输入的坐标是否在 0-200 之间,没有处理其他可能的输入错误,要增加更全面的输入验证。
在雨刷系统中,System 1 和 System 2 的代码几乎完全相同,只是 Lever 和 Dial 的实现类不同。可以将重复的代码提取到一个公共的方法中,代码只验证了用户输入的操作指令是否在 0-4 之间,没有处理其他可能的输入错误,要添加全面的输入验证。代码中通过多个条件语句来管理雨刷的状态,使得代码可读性较差,可以设置个状态类,专门管理雨刷的状态。
题目集八:航空货运管理系统
要求:
一、计费重量的确定 空运以实际重量(GrossWeight)和体积重量(VolumeWeight)中的较高者作为计费重量。 计算公式: 体积重量(kg)= 货物体积(长×宽×高,单位:厘米)÷6000 示例: 若货物实际重量为80kg,体积为120cm×80cm×60cm,则: 体积重量 =(120×80×60)÷6000=96kg 计费重量取96kg(因96kg>80kg)。
二、基础运费计算 1 费率(Rate):航空公司或货代根据航线、货物类型、市场行情等制定(如 CNY30/kg)。计算公式:基础运费 = 计费重量 × 费率
三、题目说明 本次题目模拟某客户到该航空公司办理一次货运业务的过程: 航空公司提供如下信息: 航班信息(航班号,航班起飞机场,航班降落机场,航班日期,航班最大载 重量) 2 客户填写货运订单并进行支付,需要提供如下信息: 客户信息(姓名,电话号码等) 货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等) 运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选 航班号,订单日期) 支付方式(支付宝支付、微信支付) 注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独 计费。 程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信 息报表及货物明细报表。
四、题目要求 本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开 闭原则以及合成复用原则、依赖倒转原则,设计因素:单一职责原则(20%)、里氏代换原则(20%)、开闭原则(20%)、 合成复用原则(20%)。
类图:

设计思路:
先审题了解需要哪些类,再设计一个货物抽象类,让一个货物继承它,设计一个支付的父类,微信支付和支付宝支付是它的子类,设计一个客户类,展示客户的各种信息,然后设计一个航班类,表示出航班班号和最大承重等等信息,然后设计一个货物订单类,用来输出订单各种信息并在这里实现货物运费的计算。
代码的实现具体形式

  1. 定义基础类
    • 航班类(Flight):记录航班号、起降机场、日期、最大载重等信息,提供超载判断方法(当前重量 + 新增货物重量是否超过最大载重)。
    • 客户类(Customer):存储客户 ID、姓名、电话、地址等基本信息。
    • 货物抽象类(Goods):定义货物 ID、名称、尺寸、重量等属性,通过抽象方法finalWeight()让子类实现计费重量计算(例如按实际重量或体积重量取较大值)。
    • 具体货物类(Goods1):继承货物类,实现按 “体积重量 = 长 × 宽 × 高 / 6000” 计算,并取实际重量与体积重量的较大值作为计费重量。
  2. 订单与费用计算
    • 运输信息类(DeliveryInformation):
    o 关联航班、客户、货物列表,记录订单日期、收发件人信息。
    o 计算订单总重量(累加所有货物的计费重量)。
    o 根据货物重量分档确定费率(<20kg:35 元 /kg;20-50kg:30 元 /kg;50-100kg:25 元 /kg;≥100kg:15 元 /kg),并计算总运费。
  3. 支付功能
    • 支付抽象类(PaymentWay):定义支付接口,通过策略模式实现不同支付方式。
    • 具体支付类(支付宝、微信支付):继承支付类,实现支付逻辑,输出应付金额。
  4. 主程序流程
  5. 输入客户信息:通过控制台输入客户 ID、姓名、电话、地址,创建客户对象。
  6. 输入货物信息:指定货物数量,逐个输入每件货物的 ID、名称、尺寸、重量,创建货物列表(使用 Goods1 实现)。
  7. 输入航班信息:输入航班号、起降机场、日期、最大载重,创建航班对象。
  8. 输入订单信息:输入订单 ID、日期、收发件人信息,关联航班和货物列表,创建运输信息对象。
  9. 超载检查:计算订单总重量,若超过航班最大载重,输出提示并终止程序。
  10. 输出订单详情:显示航班号、订单号、收发件人信息、总重量等。
  11. 选择支付方式:以微信支付为例,调用支付逻辑,输出应付金额。
  12. 输出货物明细:列出每件货物的计费重量、费率和运费。
    关键逻辑的说明
    • 计费重量计算:货物按 “实际重量” 与 “体积重量” 取较大值计费,体现物流行业常见规则。
    • 费率:根据货物重量自动匹配费率,重量越大费率越低,符合货运定价逻辑。
    • 超载控制:通过航班类的超载判断方法,确保订单总重量不超过航班载重限制,保障运输安全。
    代码规模:

代码复杂度如下:

整体上看,平均复杂度不是很大,我这个逻辑比较简单,但是在主函数调用了各种类,导致这个主函数复杂度过高。
从雷达图上看,可读性不强,应该多添加点注释,而且类的职责数值有点高,说明了我这个代码可能类设计过于分散。

采坑心得:
写航空货运管理系统时,我在数据处理上大意了。录入货物信息时没严格校验非法字符,像输字母这类情况,程序就无法运行了,是前期设计对输入考虑的不足。DeliveryInformation类也像个大杂烩,管信息存储还揽费用计算,有点违背单一职责,牵一发动全身。以后要把输入校验、职责划分、全面测试都分析好再编写代码。
改进建议:
我觉得可以在这个输入信息利用正则表达式进行校验,然后的话我再设计这个运输货单类的时候应该把计算费率这个和总价钱拆出来单独设计一个类,确保他们的单一职责性,并多测试这个边界范例,确保这个边界足够大,不至于越界系统崩掉。
题目集九:魔方问题和点线面再次重构问题
要求:
1.问题描述:本问题中的魔方有两种,一种是正方体魔方,一种是正三棱锥魔方,其中,正方体或正三棱锥魔方是由单元正方体或正三棱锥组成,单元正方体或正三棱锥的个数由阶数(即层数)决定,即魔方边长=阶数*单元边长。魔方如下图所示:

实现如下功能:
魔方有三个属性:颜色,阶数,类型(正方体魔方、正三棱锥魔方),程序要求输出魔方的颜色、表面积和体积。
2.在“点与线(继承与多态)”题目基础上,对题目的类设计进行重构,增加容器类保存点、线、面对象,并对该容器进行相应增、删、遍历操作。
在原有类设计的基础上,增加一个GeometryObject容器类,其属性为ArrayList类型的对象(若不了解泛型,可以不使用
增加该类的add()方法及remove(int index)方法,其功能分别为向容器中增加对象及删除第index - 1(ArrayList中index>=0)个对象
在主方法中,用户循环输入要进行的操作(choice∈[0,4]),其含义如下:
o 1:向容器中增加Point对象
o 2:向容器中增加Line对象
o 3:向容器中增加Plane对象
o 4:删除容器中第index - 1个数据,若index数据非法,则无视此操作
o 0:输入结束
输入结束后,按容器中的对象顺序分别调用每个对象的display()方法进行输出。
实现思路:
1.通过抽象类和继承实现不同形状魔方的体积计算。定义Solid抽象类表示基本几何体(立方体、正四面体),RubikCube抽象类表示魔方,子类(SquareCube、RegularPyramidCube)根据层数计算总体积。用户输入颜色、层数和单位边长,程序创建对应魔方实例并输出表面积和体积。
2.使用组合模式管理不同类型的几何元素(点、线、面)。通过Element抽象类定义统一接口,GeometryObject类作为容器管理元素集合。用户通过菜单选择添加 / 删除元素,程序最终遍历所有元素并显示信息。
不足和改进:
1.
类型转换问题:SquareCube的getVolume()方法错误地将Solid强制转换为Cube,但实际传入的可能是其他类型(如RegularPyramid),导致运行时异常。应直接使用unitSide计算,避免类型转换。
代码重复:SquareCube和RegularPyramidCube的getArea()和getVolume()逻辑几乎相同,可提取公共方法到父类,减少冗余。
输入验证缺失:未检查层数和边长是否为正数,可能导致计算结果异常。应在构造函数中添加参数校验。
抽象类设计:RubikCube的getSolid()方法返回Solid,但子类计算时实际使用的是单位边长,可考虑直接存储单位边长而非Solid实例,简化设计。
2.
封装性:GeometryObject直接暴露内部列表(getList()),破坏封装性。提供遍历方法而非直接返回列表,避免外部修改内部结构。
然后就是代码耦合度太高了,后续添加程序时,会很困难,不仅容易破坏封装性也容易导致内存泄漏,应该在设计类时就尽量减少它的耦合度。

题目集九:航空货运管理系统
要求:
一、计费重量的确定 空运以实际重量(GrossWeight)和体积重量(VolumeWeight)中的较 高者作为计费重量。 计算公式: 体积重量(kg)= 货物体积(长×宽×高,单位:厘米)÷6000 示例: 若货物实际重量为80kg,体积为120cm×80cm×60cm,则: 体积重量 =(120×80×60)÷6000=96kg 计费重量取96kg(因96kg>80kg)。
二、基础运费计算 费率(Rate):航空公司或货代根据航线、货物类型、市场行情等制定(如 CNY30/kg)。本次作业费率与货物类型有关,货物类型分为普通货物、危险货 物和加急货物三种,其费率分别为: 计算公式:基础运费 = 计费重量 × 费率 × 折扣率 其中,折扣率是指不同的用户类型针对每个订单的运费可以享受相应的折扣, 在本题中,用户分为个人用户和集团用户,其中个人用户可享受订单运费的9 折优惠,集团用户可享受订单运费的8折优惠。
三、题目说明 本次题目模拟某客户到该航空公司办理一次货运业务的过程: 航空公司提供如下信息: 航班信息(航班号,航班起飞机场,航班降落机场,航班日期,航班最大载 重量) 2 客户填写货运订单并进行支付,需要提供如下信息: 客户信息(姓名,电话号码等) 货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等) 运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选 航班号,订单日期) 支付方式(支付宝支付、微信支付、现金支付) 注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独 计费。 程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信 息报表及货物明细报表。
四、题目要求 本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开 闭原则以及合成复用原则、依赖倒转原则。
类图:

设计思路:
先审题了解需要在前面的基础上添加什么类,修改什么类,设计一个货物抽象类,让3种货物继承它,设计一个支付的父类,微信支付和支付宝支付和现金支付是它的子类,设计一个抽象客户父类,展示客户的各种信息,并有2个个体用户和集团用户继承它,然后设计一个航班类,表示出航班班号和最大承重等等信息,然后设计一个货物订单类,用来输出订单各种信息并在这里实现货物运费的计算。在添加一个费率父类,有3个费率子类继承它并且分别对应3个货物子类。再从主函数按要求格式进行输出。
代码的具体实现形式:

  1. 定义基础类
    • 航班类(Flight):记录航班号、起降机场、日期、最大载重等信息,提供超载判断方法(当前重量 + 新增货物重量是否超过最大载重)。
    • 客户类(Customer):存储客户 ID、姓名、电话、地址等基本信息。
    • 货物抽象类(Goods):定义货物 ID、名称、尺寸、重量等属性,通过抽象方法finalWeight()让子类实现计费重量计算(例如按实际重量或体积重量取较大值)。
    • 具体货物类(Goods1):继承货物类,实现按 “体积重量 = 长 × 宽 × 高 / 6000” 计算,并取实际重量与体积重量的较大值作为计费重量。
  2. 订单与费用计算
    • 运输信息类(DeliveryInformation):
    o 关联航班、客户、货物列表,记录订单日期、收发件人信息。
    o 计算订单总重量(累加所有货物的计费重量)。
    o 根据货物重量分档确定费率(<20kg:35 元 /kg;20-50kg:30 元 /kg;50-100kg:25 元 /kg;≥100kg:15 元 /kg),并计算总运费。
  3. 支付功能
    • 支付抽象类(PaymentWay):定义支付抽象父类,通过策略模式实现不同支付方式。
    • 具体支付类(支付宝、微信支付):继承支付类,实现支付逻辑,输出应付金额。
  4. 主程序流程
  5. 输入客户信息:通过控制台输入客户 ID、姓名、电话、地址,创建客户对象。
  6. 输入货物信息:指定货物数量,逐个输入每件货物的 ID、名称、尺寸、重量,创建货物列表(使用 Goods1 实现)。
  7. 输入航班信息:输入航班号、起降机场、日期、最大载重,创建航班对象。
  8. 输入订单信息:输入订单 ID、日期、收发件人信息,关联航班和货物列表,创建运输信息对象。
  9. 超载检查:计算订单总重量,若超过航班最大载重,输出提示并终止程序。
  10. 输出订单详情:显示航班号、订单号、收发件人信息、总重量等。
  11. 选择支付方式:以微信支付为例,调用支付逻辑,输出应付金额。
  12. 输出货物明细:列出每件货物的计费重量、费率和运费。
    关键逻辑的说明
    • 计费重量计算:货物按 “实际重量” 与 “体积重量” 取较大值计费。
    • 费率:根据货物重量自动匹配费率,重量越大费率越低。
    • 超载控制:通过航班类的超载判断方法,确保订单总重量不超过航班载重限制,保障运输安全。
    代码规模:

代码复杂度如下:

从表格来看,Main.main()方法的圈复杂度为 13,拥有 89 条语句,最大嵌套深度达到 54,这表明该方法中包含大量的条件判断、循环等逻辑结构,嵌套层数极深,理解和维护难度极大。与之形成鲜明对比的是,许多方法如构造函数CashPayment.CashPayment()等,复杂度仅为 1,语句数和嵌套深度也都处于较低水平,说明这些方法逻辑简单,多为基本的属性初始化操作。像DangerousGoodsRate.calculateRate()方法,复杂度为 8,意味着其内部存在较多条件分支来处理不同重量区间的费率计算。
从雷达图上看,我的可读性不高,注释很少,我的职责划分较细;我的方法比较简单,我的Main复杂度爆满,,代码复杂度有点高,维护难度很大。
采坑心得
在分析这段代码的复杂度过程中,我深刻体会到代码结构对复杂度的显著影响。Main.main()方法复杂度极高,这源于其中嵌套了大量的输入输出及业务逻辑处理。大量的条件判断和循环使得代码执行路径错综复杂,就像走进了一个迷宫,难以理清其逻辑脉络。例如,在处理客户信息、货物信息等输入时,多种类型的输入校验和对象创建逻辑交织在一起,导致代码的可读性和可维护性大幅降低。
同时,部分方法虽然简单,但在整体代码架构中,由于缺乏良好的抽象和复用机制,使得代码在功能扩展时容易出现重复代码,增加了潜在的错误风险。此外,从雷达图反映的注释占比等情况来看,代码注释不足也给理解代码意图带来困难。
改进建议
重构Main.main()方法:将其中复杂的业务逻辑进行拆分,提取出独立的方法,如将客户信息读取、货物信息读取等分别封装成单独的方法。这样可以降低方法的圈复杂度和嵌套深度,提高代码的可读性和可维护性。例如,把客户信息读取部分封装成readCustomerInfo()方法,专门负责获取和处理客户相关输入。
增强代码复用性:对于一些具有相似功能的代码片段,如不同货物类型计算最终重量的逻辑,可进一步抽象出通用的计算方法,减少重复代码。还可以通过设计模式,如策略模式,对货物费率计算等逻辑进行优化,使新增货物类型或费率规则时更加便捷,降低代码修改带来的风险。
增加注释:在关键代码段、复杂算法以及方法的功能说明处添加详细注释。注释不仅要说明代码做了什么,更要解释为什么这么做。比如在每个类和重要方法的开头,添加功能描述、参数说明以及返回值说明等,方便自己和他人理解代码意图,提高代码的可维护性。
三、 总结
通过两次题目集的实践,对抽象类、继承、多态等核心概念有了更直观的理解。例如,在实现 “航空货运系统” 时,通过抽象类Goods计算最终体重,让子类(普通货物、危险货物等)实现具体计费逻辑,体现了多态的灵活性。同时,利用组合模式(如DeliveryInformation包含Flight和Goods列表)构建了系统结构,让代码组合更加紧凑。
尝试使用策略模式(如支付方式PaymentWay的不同实现)和工厂模式(如根据输入类型创建客户对象),将变化的逻辑封装到独立类中,降低了代码耦合度。新增支付方式时只需扩展PaymentWay子类,无需修改主程序逻辑,符合开闭原则。
在处理 “几何图形管理系统” 和 “航空货运系统” 时,学会了将复杂问题拆解为多个模块(如输入处理、业务计算)。通过逐步实现每个模块的功能,再整合为完整系统,避免了直接面对复杂逻辑的混乱感。学会了分析题目和设计类。
需要进一步改进的地方:
在算法上
当前代码的货物费率计算和超载判断逻辑虽能满足基本需求,但在处理多类型货物混合计算、复杂客户折扣场景时效率不足。例如,DeliveryInformation.sumFree()方法中通过多层switch-case匹配货物类型和费率,当货物类型扩展至 5 种以上时,条件判断会显著增长,导致计算耗时增加。
在代码设计原则上
虽然代码初步遵循了单一职责原则,但DeliveryInformation类仍承担了过多职责,包括订单信息管理、重量计算、费用计算、支付逻辑关联等,导致该类与Flight、Customer、RateCalculator等类的耦合度较高。

posted @ 2025-05-20 21:07  一抹淡影  阅读(41)  评论(0)    收藏  举报