Java策略模式与工厂模式消除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分支对应一个策略类,分支多了类就多。解决办法:
- 简单的分支(少于3个)不要用策略模式
- 相似的策略可以抽象出模板方法模式,减少重复代码
- 用匿名类或Lambda表达式(Java 8+)简化简单策略
Q3:策略模式适合什么场景?
A:适合"同一个行为有多种实现,且实现可能经常新增"的场景。比如支付方式、消息发送渠道(短信/邮件/推送)、文件存储方式(本地/阿里云/七牛云)。如果分支很少且不变,用if-else反而更简单。
九、面试高频考点汇总
考点1:策略模式和简单工厂模式的区别?
答案:
- 策略模式:关注行为的替换,客户端持有Context,可以运行时切换策略。属于行为型模式。
- 简单工厂:关注对象的创建,根据参数创建不同类型的对象。属于创建型模式。
- 两者经常一起用:工厂创建策略对象,策略模式使用策略对象。
考点2:Spring中如何实现策略模式?
答案:
- 实现策略接口:多个类实现同一个接口
- 自动注入Map:
@Autowired private Map<String, Strategy> strategyMap,Spring会自动把beanName作为key注入 - 根据key获取策略:
strategyMap.get(type).execute() - 或者用
List<Strategy>配合stream过滤
@Autowired
private Map<String, PaymentStrategy> strategyMap;
public void pay(String type) {
PaymentStrategy strategy = strategyMap.get(type + "Strategy");
strategy.pay(request);
}考点3:策略模式违反了什么设计原则?如何避免?
答案:
策略模式本身不违反设计原则,但使用不当可能违反:
- 单一职责原则:如果策略类做了太多事(支付+退款+查询+回调),需要拆分成更小的策略
- 迪米特法则:如果客户端直接操作策略内部,应该通过Context封装
考点4:策略模式和状态模式的本质区别?
答案:
- 策略模式:客户端主动选择策略,策略之间是平等的替换关系。Context不持有状态,策略选择由外部决定。
- 状态模式:状态转换由内部规则驱动,状态之间有转换关系。Context持有当前状态,行为由状态决定。
考点5:如何在策略模式中实现AOP统一处理?
答案:
- 在策略接口的方法上定义切点:
@Around("execution(* com.example.strategy.*.*(..))") - 或者在Context的方法上包装AOP
- 也可以用模板方法模式,在抽象策略类中定义算法骨架,具体策略实现变化部分
十、模拟面试官提问和参考答案
场景题1:系统目前有5种支付方式,未来可能扩展到20种,怎么设计?
参考答案:
- 用策略模式封装每种支付方式,实现统一的PaymentStrategy接口
- 用Spring的依赖注入自动收集所有策略,不需要手动注册
- 策略配置存数据库,支持动态启用/禁用
- 公共逻辑(日志、异常处理、重试)用AOP统一处理
- 预留扩展点:新增支付方式只需要实现接口+配置数据库,零代码侵入
场景题2:支付接口需要支持组合优惠(满减+折扣+优惠券),怎么设计?
参考答案:
- 定义PriceCalculationStrategy接口
- 实现FullReductionStrategy(满减)、DiscountStrategy(折扣)、CouponStrategy(优惠券)
- 用装饰器模式或责任链模式组合策略:
原价 → 满减策略 → 折扣策略 → 优惠券策略 → 最终价
- 策略的执行顺序由配置决定,支持灵活调整
场景题3:策略模式重构后,怎么保证所有分支都被测试覆盖?
参考答案:
- 每个策略类独立单元测试,不依赖Spring上下文
- 工厂类测试:验证所有策略都能正确加载
- Service层测试:用Mock验证调用了正确的策略
- 集成测试:走完整支付流程
- 代码覆盖率工具(JaCoCo)检查每个策略类的覆盖率
场景题4:如果两个策略有90%的代码相同,怎么避免重复?
参考答案:
- 提取抽象基类,把公共逻辑放到基类里
- 用模板方法模式:基类定义算法骨架,子类实现变化部分
- 公共工具类:把工具方法抽到Util类中
- 组合优于继承:把公共逻辑封装成组件,策略类持有组件引用
场景题5:系统需要同时支持新老两种支付接口版本,怎么兼容?
参考答案:
- 用适配器模式包装老接口,对外暴露统一的新接口
- 策略接口定义新版本契约
- 老版本实现内部调用适配器转换
- 客户端无感知,通过配置切换版本
PaymentStrategy → WechatPayV2Strategy(新)
→ WechatPayV1Adapter → 老接口(兼容)
十一、互动话题
你在项目中遇到过最夸张的if-else有多少层?是怎么解决的?是用了策略模式,还是其他办法?欢迎在评论区分享你的"if-else地狱"经历。
参考资料
到此这篇关于Java策略模式与工厂模式消除if-else的文章就介绍到这了,更多相关java消除if-else内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
SpringBoot使用validation-api实现参数校验的示例
这篇文章主要介绍了SpringBoot使用validation-api实现参数校验的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2020-09-09
Linux环境下的Java(JDBC)连接openGauss数据库实践记录
这篇文章主要介绍了Linux环境下的Java(JDBC)连接openGauss数据库实践记录,需要的朋友可以参考下2022-11-11


最新评论