重构到设计模式案例

wen java案例 3

本文目录导读:

重构到设计模式案例

  1. 原始代码(问题所在)
  2. 第一次重构:使用 策略模式 解决分支爆炸
  3. 第二次重构:策略模式 + 简单工厂(解决客户端耦合)
  4. 第三次重构:策略模式 + 简单工厂 + 反射/注册表(解决工厂类修改)
  5. 第四次重构:引入装饰者模式(增强功能)
  6. 最终架构总览(UML 关系)
  7. 重构的思路演变

我将从一个原始代码出发,逐步展示如何重构到设计模式,让你清晰看到从“烂代码”转型为“优雅架构”的过程。

我们以经典的支付系统为例,从策略模式 出发,逐步叠加设计模式。


原始代码(问题所在)

假设我们有一个支付服务,一开始只有微信和支付宝:

public class PaymentService {
    public void pay(String type, double amount) {
        if ("wechat".equals(type)) {
            System.out.println("使用微信支付:" + amount + "元");
            // 大量微信逻辑:生成二维码、调用微信API...
        } else if ("alipay".equals(type)) {
            System.out.println("使用支付宝支付:" + amount + "元");
            // 大量支付宝逻辑:签名、调用支付宝API...
        } else {
            // 新增银行卡支付?又要增加一个 else-if
            throw new RuntimeException("不支持的支付方式");
        }
    }
}
// 客户端调用
public class Client {
    public static void main(String[] args) {
        PaymentService service = new PaymentService();
        service.pay("wechat", 100.0);
        service.pay("alipay", 200.0);
        // 如果现在要加"银行卡"支付,需要去修改 PaymentService 类
    }
}

痛点: 违反了 OCP(开闭原则),每次新增支付方式都要侵入原有代码,代码膨胀后难以维护。


第一次重构:使用 策略模式 解决分支爆炸

核心思想: 把算法(支付方式) 封装成独立的类,客户端选择策略。

定义策略接口

// 策略接口:所有支付方式的统一契约
public interface PaymentStrategy {
    void pay(double amount); // 所有支付方式都必须实现这个行为
}

实现具体策略(算法族)

// 具体策略A:微信支付
public class WechatPay implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用微信支付:" + amount + "元");
        // 微信专属复杂逻辑
    }
}
// 具体策略B:支付宝支付
public class AlipayPay implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用支付宝支付:" + amount + "元");
        // 支付宝专属复杂逻辑
    }
}
// 新增策略C:银行卡支付(不再修改旧类,直接新增类)
public class BankCardPay implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用银行卡支付:" + amount + "元");
        // 银行卡复杂逻辑
    }
}

上下文类(持有策略引用)

// 上下文:负责将请求委托给指定的策略对象
public class PaymentContext {
    private PaymentStrategy strategy; // 核心:持有策略接口
    // 通过构造器或Setter注入具体策略
    public PaymentContext(PaymentStrategy strategy) {
        this.strategy = strategy;
    }
    public void executePay(double amount) {
        strategy.pay(amount); // 委托调用
    }
}

客户端使用(策略模式)

public class Client {
    public static void main(String[] args) {
        // 不再写 if-else,直接选择策略对象
        PaymentContext context = new PaymentContext(new WechatPay());
        context.executePay(100.0);
        // 想换支付宝?直接换策略
        context = new PaymentContext(new AlipayPay());
        context.executePay(200.0);
        // 新增银行卡?客户端选择新策略即可
        context = new PaymentContext(new BankCardPay());
        context.executePay(300.0);
    }
}

成果:

  • 完全消除 if-else。
  • 新增支付方式 = 新增一个类,零修改原有代码(满足开闭原则)。
  • 策略可以运行时动态替换。

第二次重构:策略模式 + 简单工厂(解决客户端耦合)

新问题: 客户端现在直接 new 具体策略类,客户端仍然依赖具体策略,如果我们希望客户端只传一个字符串(如"wechat")就能拿到对应策略,就需要引入简单工厂

创建策略工厂

public class PaymentFactory {
    // 静态工厂方法:根据类型返回策略
    public static PaymentStrategy getPaymentStrategy(String type) {
        switch (type) {
            case "wechat":
                return new WechatPay();
            case "alipay":
                return new AlipayPay();
            case "bank":
                return new BankCardPay();
            default:
                throw new IllegalArgumentException("不支持的支付类型:" + type);
        }
    }
}

升级后的客户端(客户端更傻瓜)

public class Client {
    public static void main(String[] args) {
        // 将判断的逻辑下沉到工厂,客户端不依赖具体类
        String payType = "alipay"; // 从配置/前端获取
        PaymentStrategy strategy = PaymentFactory.getPaymentStrategy(payType);
        PaymentContext context = new PaymentContext(strategy);
        context.executePay(500.0);
    }
}

成果: 客户端只依赖工厂接口,进一步解耦了客户端与具体策略的依赖。


第三次重构:策略模式 + 简单工厂 + 反射/注册表(解决工厂类修改)

新问题: 工厂内部依然有 switch(或 if-else),新增支付方式要修改工厂类,还是不符合开闭原则,如何做到工厂也“零修改”?

方案: 使用 反射 + 配置文件注册表模式(Map>)

使用反射 + 配置文件(彻底解耦)

配置文件 payment.properties

wechat=com.example.WechatPay
alipay=com.example.AlipayPay
bank=com.example.BankCardPay

重构工厂(通过反射读取配置)

import java.io.InputStream;
import java.util.Properties;
public class PaymentFactory {
    private static final Map<String, PaymentStrategy> STRATEGY_MAP = new HashMap<>();
    static {
        // 读取配置文件
        try (InputStream input = PaymentFactory.class.getClassLoader() 
                .getResourceAsStream("payment.properties")) {
            Properties prop = new Properties();
            prop.load(input);
            // 利用反射创建实例
            for (String key : prop.stringPropertyNames()) {
                String className = prop.getProperty(key);
                Class<?> clazz = Class.forName(className);
                PaymentStrategy strategy = (PaymentStrategy) clazz.getDeclaredConstructor().newInstance();
                STRATEGY_MAP.put(key, strategy);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static PaymentStrategy getPaymentStrategy(String type) {
        return STRATEGY_MAP.get(type);
    }
}

成果:

  • 工厂类永不修改,新增支付方式只需新增类 + 在配置文件加一行。
  • 这是从“硬编码”向“元数据驱动”的架构演进。

第四次重构:引入装饰者模式(增强功能)

新需求: 支付时要做日志记录、事务控制、安全检查等横切关注点,如果直接改策略类,会破坏单一职责。

方案: 使用 装饰者模式 将增强功能动态地包装在策略对象上。

基础策略不变

创建装饰者抽象类

// 装饰者基类:同样实现策略接口,并持有被包装的策略
public abstract class PaymentDecorator implements PaymentStrategy {
    protected PaymentStrategy wrappedStrategy; // 持有被装饰的对象
    public PaymentDecorator(PaymentStrategy strategy) {
        this.wrappedStrategy = strategy;
    }
    @Override
    public void pay(double amount) {
        // 默认委托,子类可选择增强或替换
        wrappedStrategy.pay(amount);
    }
}

创建具体装饰者(增强功能)

// 具体装饰者1:日志记录
public class LoggingDecorator extends PaymentDecorator {
    public LoggingDecorator(PaymentStrategy strategy) {
        super(strategy);
    }
    @Override
    public void pay(double amount) {
        System.out.println("[日志开始] 正在支付...");
        long start = System.currentTimeMillis();
        super.pay(amount); // 调用原始支付逻辑
        long end = System.currentTimeMillis();
        System.out.println("[日志结束] 支付耗时:" + (end - start) + "ms");
    }
}
// 具体装饰者2:安全检查(腾讯云、阿里云安全风控)
public class SecurityDecorator extends PaymentDecorator {
    public SecurityDecorator(PaymentStrategy strategy) {
        super(strategy);
    }
    @Override
    public void pay(double amount) {
        System.out.println("[安全检测] 进行风险认证...");
        super.pay(amount);
        System.out.println("[安全检测] 认证通过");
    }
}

客户端:动态组合装饰

public class Client {
    public static void main(String[] args) {
        // 原始策略(微信付款)
        PaymentStrategy strategy = PaymentFactory.getPaymentStrategy("wechat");
        // 动态增强:先套日志,再套安全
        PaymentStrategy decorated = new SecurityDecorator(new LoggingDecorator(strategy));
        PaymentContext context = new PaymentContext(decorated);
        context.executePay(1000.0);
    }
}

成果:

  • 策略类保持单一职责(纯粹的业务逻辑)。
  • 日志、安全等可以任意 组合叠放,不修改原类。
  • 符合“高内聚,低耦合”。

最终架构总览(UML 关系)

PaymentStrategy (接口) <--------------- PaymentsDecorator (抽象装饰者)
      ↑                                       ↑
      |                                       |
WechatPay | AlipayPay | BankCardPay      LoggingDecorator | SecurityDecorator
客户端 (Client)  --> PaymentContext --> PaymentStrategy
                        |
                        v
                PaymentFactory (工厂,使用反射+注册表)

各模式的核心定位: | 模式 | 解决的问题 | | :--- | :--- | | 策略模式 | 将算法族封装,消除 if-else,行为可切换。 | | 简单工厂/注册表 | 将“创建算法”的过程解耦,客户端不依赖具体类。 | | 反射+配置文件 | 让工厂也完全满足开闭原则,支持动态扩展。 | | 装饰者模式 | 不修改策略类,动态给策略增加额外职责(日志、安全、缓存等)。 |


重构的思路演变

  1. 从乱到清:策略模式干掉最难看的 if-else。
  2. 从清到耦:工厂隐藏策略的创建细节,客户端不依赖具体策略。
  3. 从耦到活:反射+配置让工厂也具备扩展性,真正达到“永远不改老代码”。
  4. 从活到健:装饰者把业务外的横切逻辑剥离出来,让业务代码具备“层层打包”的能力。

通过这个案例,你可以看到:设计模式不是堆砌类,而是应对变化、理清职责的重要工具——经典设计模式组合使用往往能产生更强大的架构效果。

如果你目前有类似的、想重构的“烂代码”,可以发给我,我们一起拆解并重构到对应的设计模式!

抱歉,评论功能暂时关闭!