本文目录导读:

- 案例一:魔法数字与硬编码 → 枚举与常量
- 案例二:长方法 + 多层嵌套 → 卫语句 + 提取方法
- 案例三:重复代码 → 模板方法模式
- 案例四:上帝类 → 拆分类(单一职责)
- 案例五:过度使用 if-else 判断类型 → 策略模式 / 函数式接口
- 推荐的重构工具与辅助手段
下面从实际场景出发,为你整理了几类典型的 Java 重构案例,每个案例都包含“重构前”的问题分析和“重构后”的代码对比。
这些案例覆盖了工作中最常见的代码坏味道,可以直接参考落地。
魔法数字与硬编码 → 枚举与常量
问题分析
代码中散落着大量无意义的数字和字符串,可读性差,后期维护时极易出错。
重构前
public class OrderService {
public double calculateDiscount(String userType, double amount) {
double discount;
if (userType.equals("VIP")) {
discount = 0.8;
} else if (userType.equals("NORMAL")) {
discount = 0.95;
} else {
discount = 1.0;
}
return amount * discount;
}
}
重构后
public enum UserType {
VIP(0.8),
NORMAL(0.95),
GUEST(1.0);
private final double discountRate;
UserType(double discountRate) {
this.discountRate = discountRate;
}
public double getDiscountRate() {
return discountRate;
}
}
public class OrderService {
public double calculateDiscount(UserType userType, double amount) {
return amount * userType.getDiscountRate();
}
}
重构收益:消除了魔法值,类型安全,业务逻辑更加清晰。
长方法 + 多层嵌套 → 卫语句 + 提取方法
问题分析
方法过长,且有大量 if-else 嵌套,导致逻辑难以阅读和测试,这也是非常经典的Composed Method问题。
重构前
public double calculateSalary(Employee employee) {
double salary = 0.0;
if (employee != null) {
if (employee.getType() == EmployeeType.FULL_TIME) {
if (employee.getWorkDays() > 20) {
salary = employee.getBaseSalary() * 1.2;
} else {
salary = employee.getBaseSalary() * 1.0;
}
// 其他 FULL_TIME 相关逻辑
} else if (employee.getType() == EmployeeType.PART_TIME) {
salary = employee.getHourlyRate() * employee.getWorkHours();
if (salary < 500) {
salary = 500;
}
}
}
return salary;
}
重构后
public double calculateSalary(Employee employee) {
// 卫语句提前返回,处理异常情况
if (employee == null) {
throw new IllegalArgumentException("employee cannot be null");
}
// 重构后通过多态或枚举分派
return employee.calculateSalary();
}
进一步优化(引入策略模式):
// Employee 抽象类或接口
public abstract class Employee {
protected String name;
// ...
public abstract double calculateSalary();
}
public class FullTimeEmployee extends Employee {
@Override
public double calculateSalary() {
double salary = getBaseSalary();
if (getWorkDays() > 20) {
salary *= 1.2;
}
return salary;
}
}
public class PartTimeEmployee extends Employee {
@Override
public double calculateSalary() {
return Math.max(getHourlyRate() * getWorkHours(), 500);
}
}
重构收益:消除了嵌套,提升了可读性,且后续新增员工类型时无需修改原逻辑(开闭原则)。
重复代码 → 模板方法模式
问题分析
两个或多个方法流程结构相似,但是其中部分步骤的实现不同,大量复制粘贴会导致如果修改了一个,常常忘记修改另一个。
重构前
public class CsvReportGenerator {
public void generate() {
System.out.println("连接数据库");
// 查询 订单数据(特定逻辑)
System.out.println("查询订单数据");
System.out.println("处理为CSV格式");
System.out.println("保存文件: report.csv");
}
}
public class ExcelReportGenerator {
public void generate() {
System.out.println("连接数据库");
// 查询 库存数据(特定逻辑)
System.out.println("查询库存数据");
System.out.println("处理为Excel格式");
System.out.println("保存文件: report.xlsx");
}
}
重构后(模板方法)
public abstract class ReportGenerator {
// 模板方法定义算法骨架
public final void generate() {
connectDatabase();
queryData();
formatData();
saveFile();
}
protected void connectDatabase() {
System.out.println("连接数据库");
}
// 抽象方法,由子类实现具体差异
protected abstract void queryData();
protected abstract void formatData();
protected void saveFile() {
System.out.println("保存文件");
}
}
上帝类 → 拆分类(单一职责)
问题分析
一个类承担了过多的职责,比如既要做数据校验、又要做业务计算、还要做持久化和发送消息,导致类庞大且难以测试。
重构前
public class UserService {
public void createUser(User user) {
// 1. 参数校验
if (user.getEmail() == null || !user.getEmail().contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
// 2. 密码加密
user.setPassword(encrypt(user.getPassword()));
// 3. 保存数据库
userDao.save(user);
// 4. 发送欢迎邮件
sendWelcomeEmail(user);
}
private String encrypt(String raw) { ... }
private void sendWelcomeEmail(User user) { ... }
}
重构后(拆分)
// 1. 负责校验
@Component
public class UserValidator {
public void validate(User user) {
if (user.getEmail() == null || !user.getEmail().contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
}
}
// 2. 负责密码加密
@Component
public class PasswordEncoder {
public String encode(String raw) { ... }
}
// 3. 负责邮件通知
@Component
public class EmailService {
public void sendWelcomeEmail(User user) { ... }
}
// 4. 业务编排 / 门面
@Service
public class UserService {
private final UserValidator validator;
private final PasswordEncoder encoder;
private final UserRepository userRepository;
private final EmailService emailService;
public UserService(UserValidator validator, PasswordEncoder encoder,
UserRepository userRepository, EmailService emailService) {
this.validator = validator;
this.encoder = encoder;
this.userRepository = userRepository;
this.emailService = emailService;
}
public void createUser(User user) {
validator.validate(user);
user.setPassword(encoder.encode(user.getPassword()));
userRepository.save(user);
emailService.sendWelcomeEmail(user);
}
}
重构收益:UserService 现在只负责编排,便于测试和 Mock,且每个新类都有明确的单一职责。
过度使用 if-else 判断类型 → 策略模式 / 函数式接口
问题分析
经常根据类型或状态来切换执行逻辑,使用大量 if-else,代码冗长且每次新增类型都需要改动原方法。
重构前
public class ShippingCostCalculator {
public double calculateCost(ShippingType type, double weight) {
if (type == ShippingType.STANDARD) {
return weight * 1.5;
} else if (type == ShippingType.EXPRESS) {
return weight * 3.0 + 10;
} else if (type == ShippingType.OVERNIGHT) {
return weight * 5.0 + 20;
}
throw new IllegalArgumentException("Unknown type");
}
}
重构后(策略模式)
@FunctionalInterface
public interface ShippingStrategy {
double calculate(double weight);
}
public class ShippingCostCalculator {
private final Map<ShippingType, ShippingStrategy> strategies = new EnumMap<>(ShippingType.class);
public ShippingCostCalculator() {
strategies.put(ShippingType.STANDARD, w -> w * 1.5);
strategies.put(ShippingType.EXPRESS, w -> w * 3.0 + 10);
strategies.put(ShippingType.OVERNIGHT, w -> w * 5.0 + 20);
}
public double calculateCost(ShippingType type, double weight) {
return strategies.getOrDefault(type,
w -> { throw new IllegalArgumentException("Unknown type"); })
.calculate(weight);
}
}
重构收益:消除了 if-else 链,新增策略时只需添加 Map 项或在外部注册,避免了侵入性修改,更加灵活。
推荐的重构工具与辅助手段
| 工具/手段 | 作用 |
|---|---|
| IDE 重构快捷键(IntelliJ IDEA) | 提取方法、引入常量、移动类等自动重构 |
| SonarQube / SpotBugs | 静态扫描,发现代码坏味道 |
| 单元测试 | 重构前后确保逻辑不变(安全网) |
| ArchUnit | 架构层面约束依赖关系,防止循环依赖 |