系列文章|源码
https://github.com/tyronczt/design-mode-learn
文章目录
一、模式定义与核心思想
策略模式(Strategy Pattern) 是一种行为型设计模式,其核心思想是将一组算法或行为封装为独立的策略类,使得它们可以相互替换,且算法的变化独立于使用它的客户端。该模式通过组合替代继承,符合开闭原则(对扩展开放、对修改关闭)。
二、模式结构与Java实现
1. 核心角色
• 策略接口(Strategy):定义算法族的公共行为(如PaymentStrategy
接口)。
• 具体策略类(Concrete Strategy):实现接口的具体算法(如微信支付、支付宝支付)。
• 上下文类(Context):持有策略引用,动态切换算法(如PaymentContext
类)。
2. Java代码示例
以下以支付方式选择为例展示实现:
// 1. 策略接口(定义支付行为)
public interface IPaymentStrategy {
void pay(double amount);
}
// 2. 具体策略类(微信支付)
public class WeChatPayStrategy implements IPaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("微信支付:" + amount + "元");
}
}
// 3. 具体策略类(支付宝支付)
public class AliPayStrategy implements IPaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("支付宝支付:" + amount + "元");
}
}
// 4. 上下文类(管理策略)
public class PaymentContext {
private IPaymentStrategy strategy;
public void setStrategy(IPaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(double amount) {
if (strategy != null) {
strategy.pay(amount);
} else {
throw new IllegalStateException("未选择支付方式");
}
}
}
// 5. 客户端调用
public class Client {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();
context.setStrategy(new WeChatPayStrategy());
context.executePayment(100.0); // 输出:微信支付:100.0元
context.setStrategy(new AliPayStrategy());
context.executePayment(200.0); // 输出:支付宝支付:200.0元
}
}
三、策略模式的五大核心优势
- 消除复杂条件分支
• 传统方式需通过大量if-else
判断不同场景(如支付方式选择),策略模式将每个算法封装为独立类,代码更清晰。 - 符合开闭原则(OCP)
• 扩展性高:新增策略(如银行卡支付)只需添加新类,无需修改现有代码。
• 维护性高:修改某一策略的逻辑不会影响其他策略。 - 动态切换算法
• 运行时通过setStrategy()
方法灵活切换策略(如根据用户选择切换支付方式)。 - 算法复用与解耦
• 不同上下文可共享同一策略(如多个订单系统共用支付策略)。
• 算法实现与业务逻辑解耦,便于单元测试。 - 减少重复代码
• 公共逻辑可抽取至抽象类(如支付前的参数校验),具体策略类仅关注核心算法。
四、适用场景
- 多算法动态选择:如支付方式、数据加密算法、排序算法等。
- 业务规则频繁变化:如电商促销策略(满减、折扣、积分)。
- 替代复杂条件分支:避免多个
if-else
或switch-case
语句。
五、与其他模式的对比
模式 | 核心区别 | 典型场景 |
---|---|---|
策略模式 | 关注算法族动态切换 | 支付方式、折扣策略 |
状态模式 | 关注状态驱动的行为变化 | 订单状态流转、游戏角色 |
工厂模式 | 关注对象创建 | 策略类的实例化管理 |
六、最佳实践建议
• 结合工厂模式:使用策略工厂统一管理策略实例(如通过Map
存储策略类)。
• 默认策略:在上下文中设置默认策略以避免空指针异常。
• 策略组合:复杂场景可将多个策略组合使用(如先满减再折扣)。
总结
策略模式通过封装算法和动态切换机制,显著提升了代码的可维护性与扩展性。在Java中,结合接口与多态特性,能高效实现复杂的业务需求(如电商支付、促销活动)。其核心价值在于将“做什么”与“怎么做”分离,是应对算法灵活性和业务变化的利器。
🚀进阶版【更符合实际开发】🚀
策略+工厂模式
效果图
实际开发中只要前端传入支付类型,后端即可根据策略进行相应支付逻辑处理。
类图
控制器
@RestController
public class AdvancedClientController {
@Resource
private PaymentHandleStrategyFactory paymentHandlerFactory;
@GetMapping("/pay")
public void pay(int code, double amount) {
String paymentType = PaymentEnum.getPaymentNameByCode(code);
if (paymentType != null) {
// 根据支付方式选择支付处理器
IPaymentHandleStrategy paymentHandleStrategy = paymentHandlerFactory.getPaymentHandleStrategy(paymentType);
paymentHandleStrategy.pay(amount);
}
}
}
策略工厂
@Slf4j
@Service
public class PaymentHandleStrategyFactory {
/**
* 根据支付类型获取相应的处理策略
*
* @param type 支付类型,用于确定使用哪种策略处理支付请求
* @return IPaymentHandleStrategy 实现类,用于处理特定支付类型的策略
*/
public IPaymentHandleStrategy getPaymentHandleStrategy(String type) {
// 根据传入的类型参数选择并返回对应的支付处理策略
// AliPay+HandleStrategyImpl
IPaymentHandleStrategy iPaymentHandleStrategy = SpringContextUtil.getBean(type + "HandleStrategyImpl");
// 检查 iPaymentHandleStrategy 是否为空,并增加防御性编程
if (iPaymentHandleStrategy == null || StringUtils.isEmpty(iPaymentHandleStrategy)) {
log.warn("getDatasetHandleStrategy error. type:{}", type);
throw new RuntimeException("getDatasetHandleStrategy error. type:" + type);
}
return iPaymentHandleStrategy;
}
}
策略接口
public interface IPaymentHandleStrategy {
void pay(double amount);
}
策略实现类
@Service
public class AliPayHandleStrategyImpl implements IPaymentHandleStrategy {
@Override
public void pay(double amount) {
System.out.println("【策略+工厂】支付宝支付:" + amount + "元");
}
}
@Service
public class WeChatPayHandleStrategyImpl implements IPaymentHandleStrategy {
@Override
public void pay(double amount) {
System.out.println("【策略+工厂】微信支付:" + amount + "元");
}
}
枚举类
package com.tyron.design.mode.learn.advanced.enums;
public enum PaymentEnum {
// 微信支付
WE_CHAT_PAY(1, "weChatPay", "微信支付"),
// 支付宝支付
ALI_PAY(2, "aliPay", "支付宝支付");
private int code;
private String name;
private String desc;
PaymentEnum(int code, String name, String desc) {
this.code = code;
this.name = name;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
/**
* 根据code获取枚举常量
*/
public static PaymentEnum getPaymentEnumByCode(int code) {
for (PaymentEnum paymentEnum : PaymentEnum.values()) {
if (paymentEnum.getCode() == code) {
return paymentEnum;
}
}
return null;
}
/**
* 根据code获取枚举常量的name属性
*
* @param code
* @return
*/
public static String getPaymentNameByCode(int code) {
for (PaymentEnum paymentEnum : PaymentEnum.values()) {
if (paymentEnum.getCode() == code) {
return paymentEnum.getName();
}
}
return null;
}
}
Spring工具类
package com.tyron.design.mode.learn.util;
import lombok.NonNull;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component("SpringUtil")
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String beanName) {
if (context.containsBean(beanName)) {
return (T)context.getBean(beanName);
} else {
return null;
}
}
}
详细代码已上传 Github:https://github.com/tyronczt/design-mode-learn/tree/main/design-mode-learn-3_2