飞机货运系统blog

前言

这两次题目集要求计算空运货物的运费,主要是对类设计的考察,算法上未设置难度,第一次题目集考察了单一职责原则、里氏代换原则、开闭原则、 合成复用原则;第二次题目集在第一次的基础上增加了对继承和多态、依赖倒转原则的考察,用户、支付方式、货物均设置为抽象类,其子类均可扩展,符合开闭原则,题量中等,难度不是很大,重点在于分类及理清类与类之间的关系,设计合理的继承或组合聚合的使用。

 

设计与分析

题目集八

Good类图

 

报表内容:

 报表内容分析:

无分支语句 (if/elseswitch 等),无循环结构 (forwhile 等)

所有方法复杂度均为1,89%代码块深度≤1 (仅15行出现深度2的块),无异常处理痕迹

全文件无任何代码注释,方法命名简单但无上下文说明

14个方法平均仅含1.29条语句

 

ChargeableWeight类图

 

报表内容:

 报表内容分析:

总行数24行,有效语句16条,包含1个类,4个方法(每个类平均方法数4.00),每个方法平均1.50条语句,最大复杂度为1(位于第8行的getChargeableWeight方法)

无分支语句(Percent Branch Statements 0.0%),方法调用语句5个,最大块嵌套深度2(出现在第12行),平均块深度1.25

缺少方法说明和参数注释,无法快速理解关键逻辑:计费重量计算规则(空运行业特有的体积重/实际重比较逻辑)

 

Cost类图

报表内容:

 

报表内容分析:

setRate() 复杂度7(超过健康值5),包含多分支/循环,维护风险高

第24行代码块深度3,存在多层if\else-if\else复杂结构

7个方法平均2.29条语句,方法拆分较为合理

 

Customer类图

报表内容:

 报表内容分析:

最复杂的方法是Customer.setAddress(),复杂度为1,最大块深度是2,出现在第13行,平均块深度1.41,平均复杂度1.00

​​11个方法​​集中在1个类,存在类臃肿风险,应该≤8方法/类

 

Flight类图

 报表内容:

 报表内容分析:

平均嵌套深度1.34 层,存在整体结构扁平化的问题

平均复杂度:1.00,全文件无分支/循环语句

12个方法平均仅1.25条语句,存在过度拆分

 

OrderItem类图

报表内容:

 报表内容分析:

 16个方法/类,行业推荐≤8

仅9.2%行含注释,应该≥30%

全类复杂度=1(无分支/循环)

潜在风险:未校验num的合法性(如负值、零值)

 

Order类图

报表内容:

报表内容分析:

单类包含 ​​13个方法​​(超行业推荐值≤8)

第51行出现 ​​3层块嵌套​​(建议≤2)

最复杂方法复杂度为2,≤5,正常

第51行出现 ​​3层块嵌套​​,应该≤2

 

TransportInformation类图

 

报表内容:

 

报表内容分析:

方法设计过于简单,未有格式校验,信息格式错误风险很高,影响较为严重;

存在空指针异常风险、时效性失效风险。

 

题目集九

根据题目要求,在题目集八的基础上,将Good(商品类)、Customer(顾客类)、Payment(支付方式类)设计为了抽象类,使其具有可拓展性,符合开闭原则。

Good类及其子类实现类图如下:

符合的原则如下:

开闭原则:

通过抽象类AirGood定义基础框架(getRate为抽象方法),UrgentGood/DangerGood/CommonGood通过继承实现扩展,新增货物类型无需修改基类

多态原则:

每个子类重写getRate()方法,实现差异化的费率计算逻辑

单一职责原则:

AirGood仅关注货物基础属性和费率计算,运输验证逻辑分离到其他类

 

Payment及其子类实现类图如下:

符合原则​​:

​​策略模式:
抽象支付方式Payment定义支付接口,WeChatPay/AliPay/CashPay实现具体策略,可运行时切换支付算法

里氏替换原则:
所有子类完美替代父类Payment的pay()方法,保证接口一致性

​​依赖倒置原则​​:
高层模块依赖Payment抽象,不直接耦合具体支付实现

存在问题:

支付异常处理缺失​​,未定义支付失败异常体系

Customer及其子类实现类图如下:

符合原则​​:

​​模板方法模式​​:

Customer定义getDiscount()抽象方法,子类实现差异化折扣逻辑(个人客户与公司客户折扣率不同)

接口隔离原则:

客户属性(id/name/phone)与业务方法(getDiscount)分离,避免接口污染

​​层次化封装​​:

通过继承实现客户类型分类,基础属性在父类统一管理

存在问题:

公司客户可能需要额外属性(如税号),当前结构无法扩展

 

踩坑心得:

开始设计的时候最难的点在于分了很多类后如何把类与类之间关联起来,比如我的设计中为了符合单一职责原则,Cost类调用了CharableWeight类,CharableWeight类又调用了Good类,就需要在主方法中new一个Good对象,将其引用传给new出来的CharableWeight对象,再将引用传给new出来的Cost对象。但这个过程中传参很容易出错。

最开始提交的时候,输出的支付金额均为0,经调试发现是主方法传参出了问题

在传参给new出来的OrderItem对象时,应该把good,charWeight,cost都传过去

修改后的代码:OrderItem item = new OrderItem(i+1, good, rate, charWeight, cost, 1);

初始代码:OrderItem item = new OrderItem(i, good, 1);

初始代码只穿了Good的对象,但仅有Good的对象是无法计算出运费及费率的(原因是上述设计的调用逻辑)。

 

改进建议:

这样设计存在以下风险:

对参数顺序极其敏感,如构造函数参数顺序错误;

对象生命周期不一致,部分对象可能未完成初始化;

当类关系复杂时,存在循环依赖风险;

构造函数参数列表未能显式表达业务依赖关系;

 

可以尝试以下改进策略:

引入工厂模式以解决对象创建耦合的问题,将对象的实例化过程与使用过程解耦​​。通过引入工厂类,客户端不再直接通过new关键字创建对象,而是通过工厂方法获取所需对象。

原始代码痛点:

Good good = new UrgentGood(...);
ChargeableWeight cw = new ChargeableWeight(good);
Cost cost = new Cost(cw.calculate(), good.getRate());
OrderItem item = new OrderItem(1, good, cw, cost, ...);

 

引入工厂模式改造:

// 运输费用工厂类
public class ShippingCostFactory {
public static Cost createCost(Good good) {
ChargeableWeight cw = createChargeableWeight(good);
return new Cost(cw.calculate(), good.getRate());
}

public static ChargeableWeight createChargeableWeight(Good good) {
return new ChargeableWeight(good);
}
}

// 订单项工厂类
public class OrderItemFactory {
public static OrderItem createItem(int id, Good good, int quantity) {
Cost cost = ShippingCostFactory.createCost(good);
return new OrderItem(id, good, cost, quantity);
}
}

 

改造后调用方式:

Good good = GoodFactory.createUrgentGood(...);
OrderItem item = OrderItemFactory.createItem(1, good, 2);

 

改进的好处:

提升可维护性​​:当对象创建逻辑变更时,只需修改工厂类,无需调整所有调用方

​​增强扩展性​​:新增产品类型时,通过扩展工厂类实现开闭原则(OCP)

​​统一质量控制​​:可在工厂方法中添加统一的校验逻辑

 

总结

学到了如下内容:

​​1、分类思想​​:把不同的功能放在不同的类里(例如把运费计算和货物信息分开)

2、​​类的连接​​:学会了用"传参数"的方式让类之间产生联系

3、遵从基础原则​​:一个类只做一件事(例如Good类只存货物信息,不负责计算运费)

        通过继承实现扩展(例如普通货物和危险品货物继承同一个父类)

需要进一步改进与研究的地方:

​​传参遗漏​​:第一次做支付金额总是0,发现是漏传了运费计算需要的参数

参数顺序错误​​:构造方法参数太多时容易把顺序写反(解决方法:用Builder模式或仔细检查)​​过度拆分​​:把类拆得太细导致需要传很多参数(像把书包里每支笔都单独装袋)

要增强代码规范意识:

注释的重要性​​:刚开始完全没注释,调试时自己都看不懂

​​方法命名技巧​​:从a()改成calculateShippingFee()更易懂

要增加错误校验:通过正则表达式校验防止格式或数据类型错误

收获与成长:

编程范式的演进

从面向过程编程向面向对象编程的转型实现了代码组织方式的质变:

​​模块化封装​​:将原本集中于main方法的业务逻辑拆分为Good、ChargeableWeight、Cost等独立类,每个类封装特定领域的属性和行为

​​职责分离​​,遵循单一职责原则,例如:

1、Good类仅管理货物基础属性

2、ChargeableWeight类专注计费重量计算

3、Cost类处理运费核算

4、通过方法参数传递实现类间通信,建立Good→ChargeableWeight→Cost的链式依赖关系

 

posted @ 2025-05-24 17:05  清霖Lily  阅读(20)  评论(0)    收藏  举报