Java策略模式与工厂模式消除if-else

 更新时间:2026年06月25日 10:07:28   作者:我是一颗柠檬  
本文详细介绍了如何使用策略模式和工厂模式重构复杂的if-else逻辑,减少代码冗余,提高扩展性和可测性,通过实际代码示例,展示了从if-else地狱到优雅策略模式的完整重构过程

写在前面
说实话,我见过太多支付系统的代码,里面几十上百个if-else,新增一个支付方式要改五六个地方,测试的时候漏一个分支就上线出bug。后来我用策略模式+工厂模式重构了一个项目,代码量少了40%,新增支付方式只需要加一个类。这篇文章把这套组合拳完整教给你,附带可以直接跑的代码。

一、if-else的痛点

1.1 场景引入

假设你在做一个支付系统,支持微信支付、支付宝支付、银联支付。新手程序员通常会这样写:

@Service
public class PaymentService {
    public void pay(String paymentType, BigDecimal amount) {
        if ("WECHAT".equals(paymentType)) {
            // 微信支付逻辑... 50行
            System.out.println("调用微信支付接口");
            System.out.println("校验微信签名");
            System.out.println("处理微信回调");
        } else if ("ALIPAY".equals(paymentType)) {
            // 支付宝支付逻辑... 60行
            System.out.println("调用支付宝接口");
            System.out.println("校验支付宝签名");
            System.out.println("处理支付宝回调");
        } else if ("UNION_PAY".equals(paymentType)) {
            // 银联支付逻辑... 70行
            System.out.println("调用银联接口");
            System.out.println("校验银联证书");
            System.out.println("处理银联回调");
        } else {
            throw new IllegalArgumentException("不支持的支付方式:" + paymentType);
        }
    }
    public void refund(String paymentType, String orderNo, BigDecimal amount) {
        if ("WECHAT".equals(paymentType)) {
            // 微信退款逻辑...
        } else if ("ALIPAY".equals(paymentType)) {
            // 支付宝退款逻辑...
        } else if ("UNION_PAY".equals(paymentType)) {
            // 银联退款逻辑...
        }
        // 新增支付方式?继续加if-else吧
    }
}

1.2 代码坏味道

这种写法的问题,干过几年的人都懂:

问题后果
方法过长一个方法几百行,阅读困难
违反开闭原则新增支付方式要改原有代码
测试困难每个分支都要覆盖,漏测风险高
重复代码每种支付的公共逻辑(日志、事务)到处复制
团队协作冲突多个人改同一个文件,Git冲突不断

1.3 生活类比

if-else就像餐厅服务员记住每个老顾客的口味偏好——顾客A不吃辣、顾客B不要葱、顾客C多加醋。顾客多了根本记不住,上错菜是常态。

策略模式就像菜单上每种菜都有固定做法——宫保鸡丁有宫保鸡丁的做法,鱼香肉丝有鱼香肉丝的做法。来什么单就按什么做法做,标准化、不会乱。

二、策略模式原理

2.1 定义

策略模式(Strategy Pattern):定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。

2.2 三个角色

┌─────────────────────────────────────────┐
│              Context(上下文)             │
│  - 持有Strategy引用                       │
│  - 调用strategy.execute()                │
│  - 客户端只和Context打交道                 │
└─────────────────┬───────────────────────┘
                  │ 组合
                  ▼
┌─────────────────────────────────────────┐
│         Strategy(策略接口)              │
│         + execute()                     │
└─────────────────┬───────────────────────┘
                  │ 实现
        ┌─────────┼─────────┐
        ▼         ▼         ▼
   ┌────────┐ ┌────────┐ ┌────────┐
   │Concrete│ │Concrete│ │Concrete│
   │StrategyA│ │StrategyB│ │StrategyC│
   └────────┘ └────────┘ └────────┘
角色职责
策略接口(Strategy)定义算法的公共接口
具体策略(ConcreteStrategy)实现具体的算法
上下文(Context)持有策略引用,负责调用

2.3 策略模式基础代码

// 策略接口
public interface PaymentStrategy {
    /**
     * 支付
     * @param orderNo 订单号
     * @param amount 金额
     * @return 支付结果
     */
    PaymentResult pay(String orderNo, BigDecimal amount);
    /**
     * 获取策略类型
     */
    String getType();
}
// 具体策略:微信支付
public class WechatPayStrategy implements PaymentStrategy {
    @Override
    public PaymentResult pay(String orderNo, BigDecimal amount) {
        System.out.println("【微信支付】订单:" + orderNo + ",金额:" + amount);
        // 调用微信支付API
        // 处理签名、回调等
        return PaymentResult.success("WECHAT_TRADE_" + orderNo);
    }
    @Override
    public String getType() {
        return "WECHAT";
    }
}
// 具体策略:支付宝支付
public class AlipayStrategy implements PaymentStrategy {
    @Override
    public PaymentResult pay(String orderNo, BigDecimal amount) {
        System.out.println("【支付宝】订单:" + orderNo + ",金额:" + amount);
        // 调用支付宝API
        return PaymentResult.success("ALIPAY_TRADE_" + orderNo);
    }
    @Override
    public String getType() {
        return "ALIPAY";
    }
}
// 上下文
public class PaymentContext {
    private PaymentStrategy strategy;
    public void setStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }
    public PaymentResult executePay(String orderNo, BigDecimal amount) {
        if (strategy == null) {
            throw new IllegalStateException("支付策略未设置");
        }
        return strategy.pay(orderNo, amount);
    }
}

2.4 客户端使用

public class Client {
    public static void main(String[] args) {
        PaymentContext context = new PaymentContext();
        // 微信支付
        context.setStrategy(new WechatPayStrategy());
        context.executePay("ORDER_001", new BigDecimal("100.00"));
        // 支付宝支付
        context.setStrategy(new AlipayStrategy());
        context.executePay("ORDER_002", new BigDecimal("200.00"));
    }
}

三、工厂模式原理

3.1 三种工厂模式

工厂类型特点适用场景
简单工厂一个工厂类,根据参数创建对象类型不多,不经常扩展
工厂方法每个产品对应一个工厂产品族经常扩展
抽象工厂创建一系列相关产品需要创建产品族

3.2 简单工厂代码

public class PaymentStrategyFactory {
    private static final Map<String, PaymentStrategy> STRATEGY_MAP = new HashMap<>();
    static {
        STRATEGY_MAP.put("WECHAT", new WechatPayStrategy());
        STRATEGY_MAP.put("ALIPAY", new AlipayStrategy());
        STRATEGY_MAP.put("UNION_PAY", new UnionPayStrategy());
    }
    public static PaymentStrategy getStrategy(String type) {
        PaymentStrategy strategy = STRATEGY_MAP.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("不支持的支付方式:" + type);
        }
        return strategy;
    }
}

3.3 策略模式 + 工厂模式结合

工厂负责创建策略,上下文负责执行策略。两者配合,彻底消灭if-else。

客户端 → 工厂.getStrategy(type) → 返回具体策略
   ↓
Context.setStrategy(策略) → Context.execute()

四、实战:支付系统重构

4.1 重构前:if-else地狱

@Service
public class OldPaymentService {
    @Autowired
    private WechatPayClient wechatClient;
    @Autowired
    private AlipayClient alipayClient;
    @Autowired
    private UnionPayClient unionClient;
    /**
     * 支付接口 - 重构前
     */
    public PayResponse pay(PayRequest request) {
        String type = request.getPaymentType();
        BigDecimal amount = request.getAmount();
        String orderNo = request.getOrderNo();
        // ========== if-else地狱开始 ==========
        if ("WECHAT".equals(type)) {
            // 1. 参数校验
            if (amount.compareTo(new BigDecimal("0.01")) < 0) {
                throw new IllegalArgumentException("微信支付金额不能小于0.01");
            }
            // 2. 调用微信支付
            WechatPayResult result = wechatClient.unifiedOrder(orderNo, amount);
            // 3. 处理结果
            if (result.isSuccess()) {
                return PayResponse.success(result.getPrepayId());
            } else {
                return PayResponse.fail(result.getErrorMsg());
            }
        } else if ("ALIPAY".equals(type)) {
            // 1. 参数校验
            if (amount.compareTo(new BigDecimal("0.01")) < 0) {
                throw new IllegalArgumentException("支付宝支付金额不能小于0.01");
            }
            // 2. 调用支付宝
            AlipayResult result = alipayClient.tradePay(orderNo, amount);
            // 3. 处理结果
            if (result.isSuccess()) {
                return PayResponse.success(result.getTradeNo());
            } else {
                return PayResponse.fail(result.getErrorMsg());
            }
        } else if ("UNION_PAY".equals(type)) {
            // 1. 参数校验
            if (amount.compareTo(new BigDecimal("1")) < 0) {
                throw new IllegalArgumentException("银联支付金额不能小于1元");
            }
            // 2. 调用银联
            UnionPayResult result = unionClient.pay(orderNo, amount);
            // 3. 处理结果
            if (result.isSuccess()) {
                return PayResponse.success(result.getTn());
            } else {
                return PayResponse.fail(result.getErrorMsg());
            }
        } else {
            throw new IllegalArgumentException("不支持的支付方式:" + type);
        }
        // ========== if-else地狱结束 ==========
    }
    /**
     * 退款接口 - 同样的if-else再来一遍
     */
    public RefundResponse refund(RefundRequest request) {
        String type = request.getPaymentType();
        // ... 又是几十个if-else
        return null;
    }
    /**
     * 查询接口 - 再来一遍
     */
    public QueryResponse query(QueryRequest request) {
        String type = request.getPaymentType();
        // ... 再来几十个if-else
        return null;
    }
}

4.2 重构后:策略模式 + 工厂模式

第一步:定义策略接口

public interface PaymentStrategy {
    /**
     * 支付
     */
    PayResponse pay(PayRequest request);
    /**
     * 退款
     */
    RefundResponse refund(RefundRequest request);
    /**
     * 查询
     */
    QueryResponse query(QueryRequest request);
    /**
     * 获取策略类型标识
     */
    String getPaymentType();
    /**
     * 获取策略名称(用于日志展示)
     */
    default String getPaymentName() {
        return getPaymentType();
    }
}

第二步:实现具体策略

@Component
public class WechatPayStrategy implements PaymentStrategy {
    @Autowired
    private WechatPayClient wechatClient;
    @Override
    public PayResponse pay(PayRequest request) {
        // 微信支付特有的参数校验
        if (request.getAmount().compareTo(new BigDecimal("0.01")) < 0) {
            throw new IllegalArgumentException("微信支付金额不能小于0.01");
        }
        // 调用微信支付
        WechatPayResult result = wechatClient.unifiedOrder(
            request.getOrderNo(), 
            request.getAmount()
        );
        return result.isSuccess() 
            ? PayResponse.success(result.getPrepayId())
            : PayResponse.fail(result.getErrorMsg());
    }
    @Override
    public RefundResponse refund(RefundRequest request) {
        // 微信退款逻辑...
        System.out.println("【微信支付】执行退款,订单:" + request.getOrderNo());
        return RefundResponse.success();
    }
    @Override
    public QueryResponse query(QueryRequest request) {
        // 微信查询逻辑...
        System.out.println("【微信支付】执行查询,订单:" + request.getOrderNo());
        return QueryResponse.success();
    }
    @Override
    public String getPaymentType() {
        return "WECHAT";
    }
    @Override
    public String getPaymentName() {
        return "微信支付";
    }
}
@Component
public class AlipayStrategy implements PaymentStrategy {
    @Autowired
    private AlipayClient alipayClient;
    @Override
    public PayResponse pay(PayRequest request) {
        if (request.getAmount().compareTo(new BigDecimal("0.01")) < 0) {
            throw new IllegalArgumentException("支付宝支付金额不能小于0.01");
        }
        AlipayResult result = alipayClient.tradePay(
            request.getOrderNo(), 
            request.getAmount()
        );
        return result.isSuccess()
            ? PayResponse.success(result.getTradeNo())
            : PayResponse.fail(result.getErrorMsg());
    }
    @Override
    public RefundResponse refund(RefundRequest request) {
        System.out.println("【支付宝】执行退款,订单:" + request.getOrderNo());
        return RefundResponse.success();
    }
    @Override
    public QueryResponse query(QueryRequest request) {
        System.out.println("【支付宝】执行查询,订单:" + request.getOrderNo());
        return QueryResponse.success();
    }
    @Override
    public String getPaymentType() {
        return "ALIPAY";
    }
    @Override
    public String getPaymentName() {
        return "支付宝";
    }
}
@Component
public class UnionPayStrategy implements PaymentStrategy {
    @Autowired
    private UnionPayClient unionClient;
    @Override
    public PayResponse pay(PayRequest request) {
        // 银联特有的校验规则
        if (request.getAmount().compareTo(new BigDecimal("1")) < 0) {
            throw new IllegalArgumentException("银联支付金额不能小于1元");
        }
        UnionPayResult result = unionClient.pay(
            request.getOrderNo(), 
            request.getAmount()
        );
        return result.isSuccess()
            ? PayResponse.success(result.getTn())
            : PayResponse.fail(result.getErrorMsg());
    }
    @Override
    public RefundResponse refund(RefundRequest request) {
        System.out.println("【银联】执行退款,订单:" + request.getOrderNo());
        return RefundResponse.success();
    }
    @Override
    public QueryResponse query(QueryRequest request) {
        System.out.println("【银联】执行查询,订单:" + request.getOrderNo());
        return QueryResponse.success();
    }
    @Override
    public String getPaymentType() {
        return "UNION_PAY";
    }
    @Override
    public String getPaymentName() {
        return "银联支付";
    }
}

第三步:Spring工厂(利用依赖注入)

@Component
public class PaymentStrategyFactory implements ApplicationContextAware {
    private Map<String, PaymentStrategy> strategyMap = new HashMap<>();
    /**
     * Spring启动时自动收集所有PaymentStrategy实现类
     */
    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        Map<String, PaymentStrategy> beans = context.getBeansOfType(PaymentStrategy.class);
        for (PaymentStrategy strategy : beans.values()) {
            strategyMap.put(strategy.getPaymentType(), strategy);
            System.out.println("注册支付策略:" + strategy.getPaymentType() + 
                             " -> " + strategy.getClass().getSimpleName());
        }
    }
    /**
     * 根据类型获取策略
     */
    public PaymentStrategy getStrategy(String paymentType) {
        PaymentStrategy strategy = strategyMap.get(paymentType);
        if (strategy == null) {
            throw new IllegalArgumentException("不支持的支付方式:" + paymentType + 
                ",当前支持:" + strategyMap.keySet());
        }
        return strategy;
    }
    /**
     * 获取所有支持的支付方式
     */
    public List<String> getAllPaymentTypes() {
        return new ArrayList<>(strategyMap.keySet());
    }
}

第四步:上下文 + AOP统一处理

@Service
public class PaymentService {
    @Autowired
    private PaymentStrategyFactory strategyFactory;
    /**
     * 支付 - 核心代码只有3行!
     */
    public PayResponse pay(PayRequest request) {
        PaymentStrategy strategy = strategyFactory.getStrategy(request.getPaymentType());
        return strategy.pay(request);
    }
    /**
     * 退款 - 同样只有3行
     */
    public RefundResponse refund(RefundRequest request) {
        PaymentStrategy strategy = strategyFactory.getStrategy(request.getPaymentType());
        return strategy.refund(request);
    }
    /**
     * 查询 - 还是3行
     */
    public QueryResponse query(QueryRequest request) {
        PaymentStrategy strategy = strategyFactory.getStrategy(request.getPaymentType());
        return strategy.query(request);
    }
}

第五步:AOP统一日志和异常处理

@Aspect
@Component
public class PaymentLogAspect {
    private static final Logger logger = LoggerFactory.getLogger(PaymentLogAspect.class);
    @Around("execution(* com.example.payment.strategy.*.*(..))")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        String methodName = point.getSignature().getName();
        String className = point.getTarget().getClass().getSimpleName();
        Object[] args = point.getArgs();
        long start = System.currentTimeMillis();
        logger.info("[支付开始] {}.{}, 参数:{}", className, methodName, args);
        try {
            Object result = point.proceed();
            long cost = System.currentTimeMillis() - start;
            logger.info("[支付成功] {}.{}, 耗时:{}ms, 结果:{}", 
                       className, methodName, cost, result);
            return result;
        } catch (Exception e) {
            long cost = System.currentTimeMillis() - start;
            logger.error("[支付失败] {}.{}, 耗时:{}ms, 异常:{}", 
                        className, methodName, cost, e.getMessage());
            throw e;
        }
    }
}

4.3 重构效果对比

指标重构前重构后
PaymentService代码行数200+20
新增支付方式改动文件数5+1(新增策略类)
是否违反开闭原则
单元测试难度高(要mock所有分支)低(每个策略独立测试)
代码重复高(公共逻辑复制粘贴)低(AOP统一处理)

五、进阶:动态策略加载

5.1 从数据库加载策略配置

@Component
public class DynamicPaymentStrategyFactory {
    @Autowired
    private PaymentConfigDao configDao;
    private Map<String, PaymentStrategy> strategyMap = new ConcurrentHashMap<>();
    @PostConstruct
    public void init() {
        loadFromDatabase();
    }
    /**
     * 从数据库加载策略配置
     */
    public void loadFromDatabase() {
        List<PaymentConfig> configs = configDao.selectAll();
        for (PaymentConfig config : configs) {
            if (!config.isEnabled()) {
                continue;
            }
            // 根据配置创建策略(可以用反射加载类)
            PaymentStrategy strategy = createStrategy(config);
            strategyMap.put(config.getType(), strategy);
        }
    }
    private PaymentStrategy createStrategy(PaymentConfig config) {
        // 反射或SPI加载具体实现
        String className = config.getStrategyClass();
        try {
            Class<?> clazz = Class.forName(className);
            return (PaymentStrategy) clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("加载策略失败:" + className, e);
        }
    }
    public PaymentStrategy getStrategy(String type) {
        return strategyMap.get(type);
    }
}

5.2 从配置文件加载策略

# application.yml
payment:
  strategies:
    WECHAT:
      enabled: true
      class: com.example.payment.strategy.WechatPayStrategy
      config:
        appId: wx123456
        mchId: 1234567890
    ALIPAY:
      enabled: true
      class: com.example.payment.strategy.AlipayStrategy
      config:
        appId: 2024XXXX
@Configuration
@ConfigurationProperties(prefix = "payment")
public class PaymentProperties {
    private Map<String, StrategyConfig> strategies = new HashMap<>();
    public Map<String, StrategyConfig> getStrategies() {
        return strategies;
    }
    public static class StrategyConfig {
        private boolean enabled;
        private String className;
        private Map<String, String> config;
        // getters/setters...
    }
}

5.3 热加载策略

@Component
public class StrategyHotLoader {
    @Autowired
    private DynamicPaymentStrategyFactory factory;
    /**
     * 定时刷新策略(每5分钟)
     */
    @Scheduled(fixedRate = 300000)
    public void refresh() {
        System.out.println("刷新支付策略配置...");
        factory.loadFromDatabase();
    }
    /**
     * 管理后台手动触发刷新
     */
    public void refreshManually() {
        refresh();
    }
}

5.4 策略组合(链式执行)

/**
 * 组合策略:先打折,再支付
 */
public class CompositePaymentStrategy implements PaymentStrategy {
    private List<PaymentStrategy> strategies = new ArrayList<>();
    public void addStrategy(PaymentStrategy strategy) {
        strategies.add(strategy);
    }
    @Override
    public PayResponse pay(PayRequest request) {
        PayResponse response = null;
        for (PaymentStrategy strategy : strategies) {
            response = strategy.pay(request);
            // 可以根据结果决定是否继续
        }
        return response;
    }
    // ... 其他方法
}

六、策略模式 vs 状态模式 vs 责任链模式对比

这三种模式看起来有点像,很容易搞混。我整理了一个对比表格:

对比维度策略模式状态模式责任链模式
核心思想封装算法,互相替换封装状态,状态决定行为链式传递请求,直到有人处理
结构策略之间互相独立状态之间可以互相转换处理器之间有前后关系
客户端感知客户端主动选择策略客户端不感知状态变化客户端只发请求,不感知链
扩展性新增策略即可新增状态即可新增处理器即可
典型场景支付方式、排序算法订单状态机、审批流程过滤器链、审批链
代码复杂度

6.1 什么时候用哪个?

  • 策略模式:多种算法/方式做同一件事,客户端需要选择用哪个
  • 状态模式:对象有状态,不同状态下行为不同,状态会转换
  • 责任链模式:一个请求需要经过多个处理者,处理者有优先级

举个例子:

  • 支付方式选择 → 策略模式
  • 订单从待支付→已支付→已发货→已完成 → 状态模式
  • 请求经过鉴权→限流→日志→业务处理 → 责任链模式

七、踩坑指南

策略类过多导致类爆炸
我见过一个项目,每个接口字段的校验都写一个策略类,结果几百个类。策略模式适合"同一类问题有多种实现"的场景,不要把所有if-else都换成策略。简单的条件判断用策略就是过度设计。

工厂也变成if-else
这个坑我踩过。一开始工厂里写if-else创建策略,后来策略多了工厂也膨胀。解决办法是用Spring的依赖注入自动收集,或者用Map缓存,不要让工厂成为新的if-else聚集地。

策略共享状态问题
策略类默认是Spring单例,如果策略里有成员变量保存状态,多线程下会出问题。要么把状态放到方法参数里,要么把策略改成原型模式(@Scope(“prototype”))。

Spring循环依赖
如果策略A依赖策略B,策略B又依赖策略A,Spring启动会报错。解决办法是用@Lazy延迟注入,或者把公共逻辑抽到第三方组件里。

八、问题与解答

Q1:策略模式和工厂模式一定要一起用吗?

A:不一定。如果策略对象需要动态创建(根据运行时参数),工厂模式能帮你解耦创建逻辑。如果策略是固定的、由Spring管理的,直接用@Autowired注入到Map里就行,工厂都可以省掉。但两者配合是最常见的做法。

Q2:策略模式会不会导致类太多?

A:确实会。一个if分支对应一个策略类,分支多了类就多。解决办法:

  1. 简单的分支(少于3个)不要用策略模式
  2. 相似的策略可以抽象出模板方法模式,减少重复代码
  3. 用匿名类或Lambda表达式(Java 8+)简化简单策略

Q3:策略模式适合什么场景?

A:适合"同一个行为有多种实现,且实现可能经常新增"的场景。比如支付方式、消息发送渠道(短信/邮件/推送)、文件存储方式(本地/阿里云/七牛云)。如果分支很少且不变,用if-else反而更简单。

九、面试高频考点汇总

考点1:策略模式和简单工厂模式的区别?

答案

  • 策略模式:关注行为的替换,客户端持有Context,可以运行时切换策略。属于行为型模式。
  • 简单工厂:关注对象的创建,根据参数创建不同类型的对象。属于创建型模式。
  • 两者经常一起用:工厂创建策略对象,策略模式使用策略对象。

考点2:Spring中如何实现策略模式?

答案

  1. 实现策略接口:多个类实现同一个接口
  2. 自动注入Map@Autowired private Map<String, Strategy> strategyMap,Spring会自动把beanName作为key注入
  3. 根据key获取策略strategyMap.get(type).execute()
  4. 或者用List<Strategy>配合stream过滤
@Autowired
private Map<String, PaymentStrategy> strategyMap;
public void pay(String type) {
    PaymentStrategy strategy = strategyMap.get(type + "Strategy");
    strategy.pay(request);
}

考点3:策略模式违反了什么设计原则?如何避免?

答案
策略模式本身不违反设计原则,但使用不当可能违反:

  1. 单一职责原则:如果策略类做了太多事(支付+退款+查询+回调),需要拆分成更小的策略
  2. 迪米特法则:如果客户端直接操作策略内部,应该通过Context封装

考点4:策略模式和状态模式的本质区别?

答案

  • 策略模式:客户端主动选择策略,策略之间是平等的替换关系。Context不持有状态,策略选择由外部决定。
  • 状态模式:状态转换由内部规则驱动,状态之间有转换关系。Context持有当前状态,行为由状态决定。

考点5:如何在策略模式中实现AOP统一处理?

答案

  1. 在策略接口的方法上定义切点:@Around("execution(* com.example.strategy.*.*(..))")
  2. 或者在Context的方法上包装AOP
  3. 也可以用模板方法模式,在抽象策略类中定义算法骨架,具体策略实现变化部分

十、模拟面试官提问和参考答案

场景题1:系统目前有5种支付方式,未来可能扩展到20种,怎么设计?

参考答案

  1. 用策略模式封装每种支付方式,实现统一的PaymentStrategy接口
  2. 用Spring的依赖注入自动收集所有策略,不需要手动注册
  3. 策略配置存数据库,支持动态启用/禁用
  4. 公共逻辑(日志、异常处理、重试)用AOP统一处理
  5. 预留扩展点:新增支付方式只需要实现接口+配置数据库,零代码侵入

场景题2:支付接口需要支持组合优惠(满减+折扣+优惠券),怎么设计?

参考答案

  • 定义PriceCalculationStrategy接口
  • 实现FullReductionStrategy(满减)、DiscountStrategy(折扣)、CouponStrategy(优惠券)
  • 用装饰器模式或责任链模式组合策略:
  • 原价 → 满减策略 → 折扣策略 → 优惠券策略 → 最终价
  • 策略的执行顺序由配置决定,支持灵活调整

场景题3:策略模式重构后,怎么保证所有分支都被测试覆盖?

参考答案

  1. 每个策略类独立单元测试,不依赖Spring上下文
  2. 工厂类测试:验证所有策略都能正确加载
  3. Service层测试:用Mock验证调用了正确的策略
  4. 集成测试:走完整支付流程
  5. 代码覆盖率工具(JaCoCo)检查每个策略类的覆盖率

场景题4:如果两个策略有90%的代码相同,怎么避免重复?

参考答案

  1. 提取抽象基类,把公共逻辑放到基类里
  2. 用模板方法模式:基类定义算法骨架,子类实现变化部分
  3. 公共工具类:把工具方法抽到Util类中
  4. 组合优于继承:把公共逻辑封装成组件,策略类持有组件引用

场景题5:系统需要同时支持新老两种支付接口版本,怎么兼容?

参考答案

  • 用适配器模式包装老接口,对外暴露统一的新接口
  • 策略接口定义新版本契约
  • 老版本实现内部调用适配器转换
  • 客户端无感知,通过配置切换版本
PaymentStrategy → WechatPayV2Strategy(新)
                → WechatPayV1Adapter → 老接口(兼容)

十一、互动话题

你在项目中遇到过最夸张的if-else有多少层?是怎么解决的?是用了策略模式,还是其他办法?欢迎在评论区分享你的"if-else地狱"经历。

参考资料

设计模式:可复用面向对象软件的基础(GoF著)

到此这篇关于Java策略模式与工厂模式消除if-else的文章就介绍到这了,更多相关java消除if-else内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring IOC (DI) 依赖注入的四种方式示例详解

    Spring IOC (DI) 依赖注入的四种方式示例详解

    这篇文章主要介绍了Spring IOC (DI) 依赖注入的四种方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • java并发编程Lock锁可重入性与公平性分析

    java并发编程Lock锁可重入性与公平性分析

    这篇文章主要为大家介绍了java并发编程Lock锁可重入性与公平性分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • SpringBoot使用validation-api实现参数校验的示例

    SpringBoot使用validation-api实现参数校验的示例

    这篇文章主要介绍了SpringBoot使用validation-api实现参数校验的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • mybatis省略@Param注解操作

    mybatis省略@Param注解操作

    这篇文章主要介绍了mybatis省略@Param注解操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • 深入学习Java编程中的字符串的进阶使用

    深入学习Java编程中的字符串的进阶使用

    这篇文章主要介绍了Java编程中的字符串的高级运用,包括StringBuffer类和StringTokenizer类以及常量池的介绍,需要的朋友可以参考下
    2016-01-01
  • 使用Gradle做Java代码质量检查的方法示例

    使用Gradle做Java代码质量检查的方法示例

    这篇文章主要介绍了使用Gradle做Java代码质量检查的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-03-03
  • 什么是jsoup及jsoup的使用

    什么是jsoup及jsoup的使用

    jsoup是一款基于Java的HTML解析器,它提供了一套非常省力的API,不但能直接解析某个URL地址、HTML文本内容,而且还能通过类似于DOM、CSS或者jQuery的方法来操作数据,所以 jsoup 也可以被当做爬虫工具使用,这篇文章主要介绍了什么是jsoup及jsoup的使用,需要的朋友可以参考下
    2023-10-10
  • Linux环境下的Java(JDBC)连接openGauss数据库实践记录

    Linux环境下的Java(JDBC)连接openGauss数据库实践记录

    这篇文章主要介绍了Linux环境下的Java(JDBC)连接openGauss数据库实践记录,需要的朋友可以参考下
    2022-11-11
  • Spring中的循环依赖问题

    Spring中的循环依赖问题

    在Spring框架中,循环依赖是指两个或多个Bean相互依赖,这导致在Bean的创建过程中出现依赖死锁,为了解决这一问题,Spring引入了三级缓存机制,包括singletonObjects、earlySingletonObjects和singletonFactories
    2024-09-09
  • SpringBoot 配置文件加密的步骤

    SpringBoot 配置文件加密的步骤

    这篇文章主要介绍了SpringBoot 配置文件加密的步骤,帮助大家更好的理解和学习使用springboot框架,感兴趣的朋友可以了解下
    2021-03-03

最新评论