PTA题目集8、9总结
前言
在PTA题目集8、9中,主要考察了类设计和继承与多天的运用,通过这些题目,我学到了很多,比如:
- 掌握了Java中的继承与多态,学会使用抽象类;
- 建立了面向对象的分层设计思维,简单实现代码复用;
- 熟练了类的封装,学会用私有属性和公共方法管理数据;
- 学会使用ArrayList工具类来实现容器类的增删遍历;
- 学用父类引用调用子类方法,实现多态特性;
- 养成模块化编程习惯,遵循单一职责原则,拆分功能到不同类;
- 学会遵循开闭原则扩展功能,不修改原有逻辑;
- 学会抽象具体场景需求为类及类关系(如航空货运需求);
第一次航空货运管理系统设计
一、作业要求
航空快递以速度快、安全性高成为急件或贵重物品的首选。本题目要求对航空货运管理系统进行类设计。
通过定义枚举类型、类和方法,实现一个简单的电梯模拟系统。某航空公司“航空货运管理系统”中的空运费的计算涉及多个因素,通常包括货物重量/体积、运输距离、附加费用、货物类型、客户类型以及市场供需等。
本次作业主要考虑货物重量/体积,以下是具体的计算方式和关键要点:
1、计费重量的确定
空运以实际重量(GrossWeight)和体积重量(VolumeWeight)中的较高者作为计费重量。
计算公式:
体积重量(kg)= 货物体积(长×宽×高,单位:厘米)÷6000
示例:
若货物实际重量为80kg,体积为120cm×80cm×60cm,则:
体积重量 =(120×80×60)÷6000=96kg
计费重量取96kg(因96kg>80kg)。
2、基础运费计算
费率(Rate):航空公司或货代根据航线、货物类型、市场行情等制定(如CNY 30/kg)。本次作业费率采用分段计算方式:

公式:基础运费 = 计费重量 × 费率
3、题目说明
本次题目模拟某客户到该航空公司办理一次货运业务的过程:
航空公司提供如下信息:
航班信息(航班号,航班起飞机场所在城市,航班降落机场所在城市,航班日期,航班最大载重量)
客户填写货运订单并进行支付,需要提供如下信息:
客户信息(姓名,电话号码等)
货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)
运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选航班号,订单日期)
支付方式(支付宝支付、微信支付)
注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独计费。程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信息报表及货物明细报表。
4、题目要求
本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开闭原则以及合成复用原则
二、实现方式
通过类的封装实现数据结构化,分析完成集合存储多件货物、组合控制类串联业务流程的功能,最终完成从数据输入、计费计算、载重校验到报表输出的完整货运管理流程,注意要符合面向对象设计原则。
三、类图

Client 类
功能:封装客户基本信息,提供数据访问接口。
具体功能:
- 存储客户 ID、姓名、电话、地址等属性。
- 通过构造方法初始化客户信息。
- 提供getter/setter方法,可以访问和修改客户信息。
Flight 类
功能:管理航班基本信息,包括航班号、起降城市、日期和载重限制。
具体功能:
- 存储航班 ID、起飞机场城市、降落机场城市、航班日期和最大载重量。
- 通过构造方法初始化航班信息。
- 提供getter/setter方法,可以访问和修改航班信息。
Goods 类
功能:计算单件货物的计费重量、费率和基础运费。
具体功能:
- 存储货物 ID、名称、长、宽、高和实际重量。
- 进行计费重量计算,类中的costWeight()方法比较实际重量与体积重量,取较大值作为计费重量。
- 进行费率计算,类中的Rate()方法根据计费重量分四段计算费率。
- 进行类中的基础运费计算,money()方法返回计费重量与费率的乘积。
Order 类
功能:管理货运订单的基本信息,包括订单号、日期、收发件人信息。
具体功能:
- 存储订单 ID、日期、发件人 / 收件人的姓名、电话、地址。
- 通过构造方法初始化订单信息。
- 提供getter/setter方法,可以访问和修改订单信息。
Control 类
功能:整合业务逻辑,控制订单处理流程,生成报表。
具体功能:
- 组合Client、Flight、Goods列表和Order对象。
- 通过outWeight()方法计算所有货物的总计费重量,判断是否超过航班最大载重量。
- 通过print()方法格式化输出订单信息和货物明细,包括客户信息、航班号、订单号、日期、收发件人信息、订单总重量、支付金额以及货物明细表格。
Main 类
功能:程序入口,处理用户输入,创建对象并触发业务流程。
具体功能:
- 通过Scanner按顺序读取客户、货物、航班、订单信息。
- 根据输入数据实例化Client、Goods列表、Flight、Order对象。
- 将所有对象注入Control类,调用print()方法生成并输出报表。
类间关系
- Main类负责驱动整个流程,输入解析和对象组装。
- Control类是核心业务控制器,依赖Goods、Client、Flight、Order等类完成校验和报表生成。
- Goods类只独立计算单件货物费用,不依赖其他类。
- Client、Flight、Order类是纯数据类,负责提供数据封装和访问接口。
四、代码分析

不足:
- 代码缺乏复用机制,客户、航班等类的属性访问逻辑重复,如果抽象公共基类或工具方法,后续添加新的功能会更方便。
- 代码质量方面,方法复杂,Goods.Rate的条件判断和Control.print的长输出语句导致此次程序设计的代码复杂度过高。
- 代码中集合遍历冗余,print和outWeight方法两次遍历goods,可合并优化。
五、踩坑心得
- 第一次编写完代码后,我发现我的代码存在非零返回的问题,通过调试,我发现是因为我在代码中使用 Scanner读取输入时,混合使用了nextLine()以及其他如nextInt()等方法,我在网上查找不能混用两个方法的原因,发现是因为在读取完int类型数据,如input.nextInt()后,直接调用input.nextLine()会读取残留的换行符而非实际输入内容,导致发生非零返回,所以我需要在每次调用nextInt()、nextDouble() 等方法后,调用一次 input.nextLine() 来消费残留的换行符。
- 修改了错误后,我的代码的输出结果并不正确,我发现客户信息的输出有问题,经过调试,发现是因为客户类的构造方法的参数顺序与类定义不一致。
在客户类中,类定义顺序为address, id, name, phone;
但代码中传入顺序为id, name, phone, address;
所以只有address字段的赋值是正确的,id、name、phone字段则会被错误赋值,如id字段实际存储的是name。
这导致client.getName()返回的是错误的值,而非用户的输入,进而导致输出中的客户信息展示错误。 - 在对上述错误进行修改后,发现输出依然错误,我仔细查看了一遍代码,发现是输出的目标地点的调用调用成了出发地点,导致输出错误,在检查的过程中因为这个错误花了太多的时间,非常不应该!!!今后要避免此类粗心导致的错误。
第二次航空货运管理系统设计
一、作业要求
航空快递以速度快、安全性高成为急件或贵重物品的首选。本题目要求对航空货运管理系统进行类设计,某航空公司“航空货运管理系统”中的空运费的计算涉及多个因素,通常包括货物重量/体积、运输距离、附加费用、货物类型、客户类型以及市场供需等。
本次作业主要考虑货物重量/体积,以下是具体的计算方式和关键要点:
1、计费重量的确定
空运以实际重量(GrossWeight)和体积重量(VolumeWeight)中的较高者作为计费重量。
计算公式:
体积重量(kg)= 货物体积(长×宽×高,单位:厘米)÷6000
示例:
若货物实际重量为80kg,体积为120cm×80cm×60cm,则:
体积重量 =(120×80×60)÷6000=96kg
计费重量取96kg(因96kg>80kg)。
2、基础运费计算
费率(Rate):航空公司或货代根据航线、货物类型、市场行情等制定(如CNY30/kg)。本次作业费率与货物类型有关,货物类型分为普通货物、危险货物和加急货物三种,其费率分别为:

计算公式:基础运费 = 计费重量 × 费率 × 折扣率
其中,折扣率是指不同的用户类型针对每个订单的运费可以享受相应的折扣,在本题中,用户分为个人用户和集团用户,其中个人用户可享受订单运费的9折优惠,集团用户可享受订单运费的8折优惠。
3、题目说明
本次题目模拟某客户到该航空公司办理一次货运业务的过程:
航空公司提供如下信息:
航班信息(航班号,航班起飞机场,航班降落机场,航班日期,航班最大载重量)
客户填写货运订单并进行支付,需要提供如下信息:
客户信息(姓名,电话号码等)
货物信息(货物名称,货物包装长、宽、高尺寸,货物重量等)
运送信息(发件人姓名、电话、地址,收件人姓名、电话、地址,所选航班号,订单日期)
支付方式(支付宝支付、微信支付、现金支付)
注:一个货运订单可以运送多件货物,每件货物均需要根据重量及费率单独计费。程序需要从键盘依次输入填写订单需要提供的信息,然后分别生成订单信息报表及货物明细报表。
4、题目要求
本次题目重点考核面向对象设计原则中的单一职责原则、里氏代换原则、开闭原则以及合成复用原则、依赖倒转原则。
二、实现方式
通过定义客户、货物、航班、订单等类,结合多态机制,根据货物类型计算不同费率、客户类型应用折扣,以实际重量与体积重量的较高者作为计费重量,完成基础运费计算,然后从读取用户输入信息生成订单,验证航班载重后输出订单报表及货物明细,同时整体设计遵循单一职责、开闭原则等面向对象设计原则。
三、类图

Main 类
功能:程序入口,处理输入、创建对象、驱动业务流程。
具体功能:
- 读取客户、货物、航班、订单、支付方式等输入数据。
- 根据货物类型创建对应Goods子类实例,存入集合。
- 根据支付方式创建对应Pay子类实例。
- 实例化Client、Flight、Order、Control对象,调用Control.print(pay)方法输出。
Pay 抽象类及子类
功能:封装支付方式,通过多态输出支付名称。
具体功能:
Pay类定义了抽象方法pay(),子类重写返回对应支付字符串,支持支付方式扩展(新增子类即可)。
Control 类
功能:业务控制中心,处理载重校验、运费计算、报表输出。
具体功能:
- 类中outWeight()方法可以校验订单总重量是否超航班载重。
- 类中print(Pay pay)方法负责进行检验并输出,若超重,输出提示;否则,格式化输出订单信息、支付方式、总重量、总运费,然后遍历货物,输出明细。
- 负责组合业务对象(如客户、航班、货物、订单),集中处理流程。
Client 类
功能:封装客户信息,支持类型区分(用于运费折扣)。
具体功能:
- 存储客户属性,提供getter/setter方法,可以访问和修改客户信息。
- 判断客户类型,会影响运费计算。
Flight 类
功能:封装航班信息,用于载重校验。
具体功能:
- 存储航班属性,提供getter/setter方法,可以访问和修改航班信息。
- 与订单重量对比,确保业务规则。
Goods抽象类及其子类
功能:封装货物信息,多态实现费率计算,支持计费和运费的计算。
具体功能:
- 定义公共属性和抽象方法 Rate()(负责让子类实现费率)。
- Goods类中公共方法costWeight()负责计算计费重量、money(Client client)负责按客户类型计算折扣运费,调用 Rate())。
- 子类中重写Rate(),可以实现不同货物的费率规则,并且支持扩展。
Order类
功能:封装订单信息,作为报表数据源。
具体功能:
- 存储订单属性,提供getter/setter方法,可以访问和修改订单信息。
- 被Control.print()读取,生成订单报表基础信息。
类间关系
- Main类:负责输入解析、创建所有业务类实例,驱动程序流程。
- Control类:依赖Client进行折扣计算、Flight进行载重校验、Goods集合进行费用计算、Order进行订单数据读取,负责处理业务逻辑,最后调用Pay多态输出支付方式。
- Goods类及子类:父类定义货物计算逻辑,子类重写费率,并且依赖Client类型计算折扣运费。
- Pay类及子类:父类定义支付接口,子类实现具体支付名称,并且供Control多态调用。
- Client、Flight、Order类:纯数据类,封装属性,为其他类提供数据支持。
四、代码分析

不足:
- 复用机制缺失,客户、航班、订单类的属性访问逻辑重复,未抽象公共基类或工具方法,新增功能时代码冗余,开发效率低。
- 方法逻辑复杂,Goods.Rate()的if-else和Control.print()的长输出语句,导致代码可读性差、维护难,代码复杂度高。
- 业务规则分散,费率、运费计算逻辑分散,未集中管理,修改需多处调整。
- print和outWeight两次遍历goods,导致集合遍历冗余,可合并一次计算重量和运费,优化性能。
五、踩坑心得
- 在编写完代码后尝试运行,然后发现结果错误,无法对各个类型进行判定,最后发现是因为我在判断goodsType等类型判断时使用了==比较字符串(如if(goodsType=="Normal")),通过学习,我发现实际上字符串比较应该使用equals(),应该将所有的字符串比较都改为使用equals()进行比较。
- 在代码完成的基础上,我本来想增加一个OrderDefinite类来将Goods对象封装为订单明细项,分离职责,但是在写完之后发现因为一开始没有想到这个类,添加以后代码逻辑会出现问题,没有办法对类型进行判定,最后放弃了,以后需要在一开始就做好完整的类设计,避免再出现这种情况。
总结
通过这两次作业,我初步掌握了面向对象设计的核心原则与编程技巧,但在设计模式应用、代码复杂度控制及业务扩展能力方面仍有提升空间。后续需通过更多练习,学会结合设计模式优化类结构,强化代码复用与可维护性,同时深入理解六大设计原则在复杂场景中的综合运用,逐步形成更成熟的面向对象设计思维。
浙公网安备 33010602011771号