《Java实战:机票折扣计算系统的防御式编程与代码优化》

6 阅读3分钟

一、案例背景:机票价格计算需求

核心业务规则

  • 旺季(5-10月)
    🔹 头等舱:原价 × 90%
    🔹 经济舱:原价 × 85%
  • 淡季(1-4月、11-12月)
    🔹 头等舱:原价 × 70%
    🔹 经济舱:原价 × 65%
  • 输入约束
    🔸 月份:1-12的整数
    🔸 舱位类型:仅支持"头等舱"/"经济舱"
    🔸 原价:正数(支持小数)

二、代码全景解析(优化版)

package com.insightfuture.test;

import java.util.Scanner;

public class BuyPlaneTickets {
    // 定义常量折扣率
    private static final double HIGH_SEASON_FIRST_CLASS = 0.9;
    private static final double HIGH_SEASON_ECONOMY = 0.85;
    private static final double LOW_SEASON_FIRST_CLASS = 0.7;
    private static final double LOW_SEASON_ECONOMY = 0.65;

    public static void main(String[] MakeItPossible) {
        Scanner sc = new Scanner(System.in);
        int month;
        String type;
        double originalPrice;
        double price = -1;

        // 输入月份并验证
        while (true) {
            System.out.println("请输入月份(1-12):");
            month = sc.nextInt();
            if (month >= 1 && month <= 12) break;
            System.out.println("月份无效,请重新输入!");
        }

        // 输入座位类型并验证
        while (true) {
            System.out.println("请输入座位类型(头等舱/经济舱):");
            type = sc.next();
            if (type.equals("头等舱") || type.equals("经济舱")) break;
            System.out.println("座位类型无效,请重新输入!");
        }

        // 输入原价
        System.out.println("请输入原价:");
        originalPrice = sc.nextDouble();

        // 计算折扣价
        if (month >= 5 && month <= 10) {
            // 旺季
            price = getPrice(type, price, originalPrice, HIGH_SEASON_FIRST_CLASS, HIGH_SEASON_ECONOMY);
        } else {
            // 淡季
            price = getPrice(type, price, originalPrice, LOW_SEASON_FIRST_CLASS, LOW_SEASON_ECONOMY);
        }
        System.out.println(month + "月份" + type + "机票原价:" + originalPrice + "元,折扣后价格为:" + price);
    }

    /*
        getPrice方法:根据条件规则计算后的实际价格
     */
    private static double getPrice(String type, double price, double originalPrice, double highSeasonFirstClass, double highSeasonEconomy) {
        switch (type) {
            case "头等舱":
                price = originalPrice * highSeasonFirstClass;
                break;
            case "经济舱":
                price = originalPrice * highSeasonEconomy;
                break;
        }
        return price;
    }
}

三、四大核心优化策略

1. 防御式输入验证体系

验证类型实现方式防御效果
月份验证while循环 + 范围判断杜绝13月等非法值
舱位验证白名单校验(equals)过滤"商务舱"等无效输入
价格验证Scanner.nextDouble()兼容小数输入
// 舱位类型验证示例
private static String validateType(Scanner sc) {
    while (true) {
        System.out.print("请输入舱位(头等舱/经济舱): ");
        String input = sc.next();
        if (input.equals("头等舱") || input.equals("经济舱")) {
            return input;
        }
        System.out.println("⚠️ 舱位类型错误!");
    }
}

2. 魔法值消除术

优化对比表

代码版本折扣率代码示例可维护性可读性
原始版本price * 0.9
优化版price * HIGH_SEASON_FIRST

常量命名技巧

  • 季节标识(HIGH/LOW_SEASON)
  • 舱位类型(FIRST/ECONOMY)
  • 语义化组合 → 一眼看懂业务含义

3. 计算逻辑分层封装

模块化设计图解

               main()
                │
                ├─输入验证层───validateMonth()
                │           ├─validateType()
                │           └─validatePrice()
                │
                └─业务计算层───calculateFinalPrice()
                              ├─季节判断
                              └─舱位费率匹配

方法设计原则

  • 单一职责:每个方法只做一件事
  • 开闭原则:新增舱位不改动主逻辑

4. 专业级输出优化

// 原始输出
System.out.println("5月份经济舱价格...");

// 优化后输出
System.out.printf("[%02d月] %s 原价: ¥%.2f → 折后价: ¥%.2f%n", 
                 month, type, price, finalPrice);

优化点

  • %02d:月份统一显示为两位数(如"05月")
  • %.2f:价格强制保留两位小数
  • ¥符号:符合财务规范

四、扩展空间与高阶优化方向

1. 精度问题解决方案

金融计算致命陷阱

// 错误示例:double计算
0.1 + 0.2 = 0.30000000000000004

// 正确方案:BigDecimal
BigDecimal price = new BigDecimal("1000.00");
BigDecimal result = price.multiply(new BigDecimal("0.9"));

2. 设计模式改造

策略模式应用场景

// 定义策略接口
interface DiscountStrategy {
    double getFirstClassRate();
    double getEconomyRate();
}

// 实现具体策略
class HighSeasonStrategy implements DiscountStrategy {
    public double getFirstClassRate() { return 0.9; }
    public double getEconomyRate() { return 0.85; }
}

五、新手常见误区指南

误区1:过度信任用户输入

反面教材

// 直接使用未验证的输入
int month = sc.nextInt(); 
calculatePrice(month, type, price); // 若month=13会怎样?

正确认知:所有外部输入都可能是恶意的!

误区2:复制粘贴的代价

代码坏味道

if (month >=5 && month <=10) {
    // 计算旺季价格
} else {
    // 计算淡季价格(重复相同switch结构)
}

重构建议:提取费率配置到Map结构

Map<String, Double> rates = isHighSeason ? highSeasonRates : lowSeasonRates;
return price * rates.get(type);

六、实战总结

关键收获清单

  • 🛡️ 防御式编程:输入验证是系统的第一道防线
  • 🧩 模块化设计:拒绝意大利面条式代码
  • 📚 常量管理:让魔法数字无处遁形
  • 💡 可扩展思维:写出适应变化的代码

#Java #代码优化 #防御式编程 #实战教程