第二次blog作业

题目集8~9总结性Blog

前言

题目集8和9围绕航空货运管理系统展开,主要考察面向对象设计思想、集合框架应用、异常处理及复杂业务逻辑的建模能力。题目集8包含5道基础题,侧重于类的定义、基础数据封装及简单业务逻辑实现;题目集9的2道题目则要求对系统进行功能扩展(如动态计价、航班调度优化),难度显著提升。整体来看,这两次题目集从单一对象建模逐步过渡到多模块协作,对代码架构设计能力提出了较高要求。


审题与逻辑构思

审题:从需求到边界条件

在题目集8的“货物与航班匹配”问题中,初次审题时对“货物类型限制”的理解存在偏差。题目要求“易碎品只能装载到特定航班”,但未明确航班属性的定义方式。误区:最初试图在Flight类中添加布尔字段isFragileAllowed,但后续发现题目隐含条件是根据航班编号前缀判断(如FR开头航班允许装载易碎品)。这一疏漏导致首次提交时多个测试用例失败。

改进方法

  1. 标注关键词:用高亮标记需求中的限制条件(如“仅限”“必须”)。
  2. 举例验证:针对模糊描述,构造多个输入样例(如航班FR001、CA002)模拟业务场景。

逻辑构思:从功能到类设计

题目集9的“动态计价策略”要求根据货物类型、重量区间计算运费。最初设计时,试图在Order类中直接嵌入计价逻辑,导致代码臃肿:

// 错误示范:硬编码计价逻辑  
public double calculateFee() {  
    if (cargo.getType().equals("Fragile")) {  
        return basePrice * 1.5;  
    } else if (weight > 100) {  
        return basePrice * 0.9;  
    }  
    // ...  
}  

反思:未考虑计价策略可能动态变化(如新增“紧急件”类型),违背开闭原则。最终采用策略模式,将计价规则抽象为PricingStrategy接口,每个策略独立实现calculate方法。


代码实现与调试

代码实现:集合框架的选择

题目集8中需要统计每个航班的货物分布,最初使用List<Cargo>存储:

class Flight {  
    private List<Cargo> cargoList; // 无法快速统计某类货物数量  
}  

问题:查询特定类型货物数量需要遍历整个列表,时间复杂度为O(n)。优化后改用Map<CargoType, Integer>

private Map<CargoType, Integer> cargoCountMap = new HashMap<>();  
// 查询效率提升至O(1)  
public int getCountByType(CargoType type) {  
    return cargoCountMap.getOrDefault(type, 0);  
}  

调试:定位隐蔽的并发问题

在题目集9的航班容量更新逻辑中,发现当多个订单同时分配至同一航班时,偶尔出现容量超限。通过日志插桩定位问题:

// 添加调试日志  
public boolean reserveCapacity(int weight) {  
    System.out.println("当前容量:" + remainingCapacity + ",申请重量:" + weight);  
    if (remainingCapacity >= weight) {  
        remainingCapacity -= weight;  
        return true;  
    }  
    return false;  
}  

日志输出

当前容量:100,申请重量:80 → 剩余20  
当前容量:100,申请重量:30 → 超限  

原因:未对reserveCapacity方法加锁,导致并发状态下数据不一致。通过synchronized关键字解决:

public synchronized boolean reserveCapacity(int weight) { ... }  

优化实现与性能提升

算法优化:航班调度的优先级队列

题目集9要求为订单分配“最优航班”,最初实现采用遍历列表并排序:

List<Flight> candidates = new ArrayList<>();  
for (Flight f : flights) {  
    if (f.canLoad(order)) candidates.add(f);  
}  
candidates.sort((a, b) -> a.getPriority() - b.getPriority());  

性能问题:每次分配需排序,时间复杂度O(n log n)。改用PriorityQueue优化:

PriorityQueue<Flight> flightQueue = new PriorityQueue<>(Comparator.comparingInt(Flight::getPriority));  
flightQueue.addAll(flights.stream().filter(f -> f.canLoad(order)).toList());  
Flight bestFlight = flightQueue.poll(); // O(1)获取最高优先级航班  

测试数据对比(10000次分配):

  • 原方案:平均耗时 320ms
  • 优化后:平均耗时 150ms

内存优化:避免重复对象创建

在解析输入数据时,频繁创建Cargo对象导致内存占用高。通过引入对象池减少GC压力:

public class CargoPool {  
    private static Map<String, Cargo> cache = new HashMap<>();  

    public static Cargo getCargo(String id, double weight, CargoType type) {  
        String key = id + type.toString();  
        return cache.computeIfAbsent(key, k -> new Cargo(id, weight, type));  
    }  
}  

题型反思与改进方向

题型反思

  1. 过度设计陷阱:在题目集8中过早引入“订单状态机”,但实际上题目仅需简单状态标记(如“已分配”“未分配”),导致代码复杂度高于必要。
  2. 边界条件遗漏:题目集9的运费计算未考虑重量为0的情况,引发ArithmeticException异常。

改进方向

  1. 单元测试覆盖:针对每个方法编写测试用例,使用JUnit参数化测试验证边界值。
    @ParameterizedTest  
    @CsvSource({"100, 0.9", "150, 0.8", "50, 1.0"})  
    void testPricingStrategy(int weight, double expectedRate) {  
        Cargo cargo = new Cargo("C1", weight, CargoType.STANDARD);  
        assertEquals(expectedRate, strategy.calculateRate(cargo));  
    }  
    
    

总结

知识收获

  1. 掌握面向对象设计原则(如单一职责、开闭原则)在实际项目中的应用。
  2. 学会通过性能分析工具(如JProfiler)定位代码瓶颈。

技能提升

  • 复杂业务逻辑的拆解能力:从需求中提取关键实体、关系及约束条件。
  • 调试技巧进阶:利用条件断点、表达式求值(在IDEA中)快速定位问题。

未来学习方向

  1. 深入理解并发编程模型(如ReentrantLock与synchronized的差异)。
  2. 学习领域驱动设计(DDD)思想,提升复杂系统建模能力。

最后感悟
编码不仅是将需求翻译为代码,更是一场与细节的较量。从最初的“能运行就行”到现在的“追求优雅与高效”,每一次调试和重构都让我深刻体会到:优秀的代码是不断打磨的产物,而耐心与严谨是开发者最宝贵的品质。

posted @ 2025-05-25 21:20  windstreet  阅读(26)  评论(0)    收藏  举报