一、概念、作用、运转流程与机制
1.1 概念
策略模式是一种行为型设计模式,它定义了一系列算法(或业务规则),将每个算法封装在独立的类中,并使它们可以互相替换。策略模式让算法的变化独立于使用它的客户端,即客户端可以根据需要动态选择不同的算法,而不需要修改原有代码。
1.2 作用
-
消除多重条件判断 :避免代码中出现大量的
if-else或switch-case分支。 -
遵循开闭原则:新增算法时只需增加新的策略类,无需修改上下文或客户端代码。
-
提高代码复用性与可维护性:将算法封装成独立的类,便于单元测试和复用。
-
支持动态切换算法:在运行时可以灵活更换策略,而不需要重新创建上下文对象。
1.3 角色组成
| 角色 | 说明 |
|---|---|
| Strategy(策略接口) | 定义所有算法必须实现的公共方法。 |
| ConcreteStrategy(具体策略) | 实现策略接口,封装具体的算法逻辑。 |
| Context(上下文) | 持有策略接口的引用,负责调用具体策略的方法。客户端通过上下文来执行策略。 |
1.4 运转流程
-
客户端创建一个具体的策略对象(如
AliPayStrategy)。 -
将该策略对象设置到上下文(
PaymentContext)中。 -
上下文调用策略对象的公共方法,执行算法。
-
如果需要更换算法,客户端只需替换上下文中持有的策略对象即可。
1.5 机制原理
-
组合优于继承:上下文通过组合(持有策略接口引用)而不是继承来扩展行为。
-
多态:策略接口定义方法,具体策略实现该方法,上下文依赖接口编程,实现运行时绑定。
-
委派:上下文将具体算法执行委派给所持有的策略对象。
二、实际场景应用
| 领域 | 示例场景 |
|---|---|
| 电商支付 | 支付宝、微信支付、银行卡支付、余额支付等不同支付方式。 |
| 促销活动 | 满减、打折、优惠券、赠品、积分抵扣等不同促销策略。 |
| 数据校验 | 手机号校验、邮箱格式校验、身份证校验、密码强度校验等。 |
| 排序算法 | 快速排序、归并排序、冒泡排序、堆排序等不同排序策略。 |
| 文件压缩 | ZIP、RAR、GZIP 等不同压缩算法。 |
| 游戏AI | 不同攻击方式(近战、远程、魔法)、不同移动方式(步行、飞行、传送)。 |
| 日志记录 | 文件日志、数据库日志、控制台日志、远程日志等。 |
| 负载均衡 | 轮询、随机、加权轮询、一致性哈希等不同均衡策略。 |
三、实战 Demo(以支付方式为例)
3.1 策略接口
// 支付策略接口
public interface PaymentStrategy {
void pay(double amount); // 支付方法
}
3.2 具体策略实现
// 支付宝支付
public class AliPayStrategy implements PaymentStrategy {
private String account;
public AliPayStrategy(String account) {
this.account = account;
}
@Override
public void pay(double amount) {
System.out.printf("使用支付宝账户[%s]支付 %.2f 元%n", account, amount);
}
}
// 微信支付
public class WechatPayStrategy implements PaymentStrategy {
private String openId;
public WechatPayStrategy(String openId) {
this.openId = openId;
}
@Override
public void pay(double amount) {
System.out.printf("使用微信用户[%s]支付 %.2f 元%n", openId, amount);
}
}
// 信用卡支付
public class CreditCardStrategy implements PaymentStrategy {
private String cardNo;
private String cvv;
public CreditCardStrategy(String cardNo, String cvv) {
this.cardNo = cardNo;
this.cvv = cvv;
}
@Override
public void pay(double amount) {
System.out.printf("使用信用卡[%s]支付 %.2f 元%n", cardNo, amount);
}
}
3.3 上下文(Context)
// 支付上下文
public class PaymentContext {
private PaymentStrategy strategy;
// 设置策略(运行时动态改变)
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
// 执行支付
public void executePayment(double amount) {
if (strategy == null) {
throw new IllegalStateException("未设置支付策略");
}
strategy.pay(amount);
}
}
3.4 客户端使用
public class Client {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();
// 使用支付宝支付
context.setStrategy(new AliPayStrategy("alice@example.com"));
context.executePayment(99.9);
// 动态切换为微信支付
context.setStrategy(new WechatPayStrategy("wxid_abc123"));
context.executePayment(199.5);
// 再切换为信用卡支付
context.setStrategy(new CreditCardStrategy("6222****1234", "123"));
context.executePayment(299.0);
}
}
输出:
使用支付宝账户[alice@example.com]支付 99.90 元
使用微信用户[wxid_abc123]支付 199.50 元
使用信用卡[6222****1234]支付 299.00 元
3.5 扩展:使用枚举 + 工厂模式(可选)
为避免客户端直接 new 具体策略,可以结合工厂模式或枚举来封装策略创建逻辑,进一步提高封装性。
// 策略类型枚举
public enum PayType {
ALI_PAY, WECHAT_PAY, CREDIT_CARD
}
// 策略工厂
public class PaymentStrategyFactory {
public static PaymentStrategy getStrategy(PayType type, Map<String, String> params) {
switch (type) {
case ALI_PAY:
return new AliPayStrategy(params.get("account"));
case WECHAT_PAY:
return new WechatPayStrategy(params.get("openId"));
case CREDIT_CARD:
return new CreditCardStrategy(params.get("cardNo"), params.get("cvv"));
default:
throw new IllegalArgumentException("不支持的支付类型");
}
}
}
四、优缺点对比
4.1 优点
| 优点 | 说明 |
|---|---|
| 开闭原则 | 新增策略无需修改上下文和客户端代码,只需添加新的策略类。 |
| 消除条件判断 | 避免大量 if-else 或 switch-case,代码更清晰。 |
| 提高内聚性 | 每个算法独立封装,职责单一,便于测试和维护。 |
| 动态替换 | 运行时可以灵活切换算法,无需重新构建上下文。 |
| 复用性 | 策略类可以在不同的上下文中复用。 |
4.2 缺点
| 缺点 | 说明 |
|---|---|
| 类数量膨胀 | 每个具体策略对应一个类,当算法很多时会产生大量类。 |
| 客户端必须了解策略 | 客户端需要知道有哪些策略并选择合适的策略,增加了客户端的复杂度。 |
| 策略间无法直接通信 | 不同策略之间如果存在公共数据或逻辑,可能需要额外传递上下文或使用其它模式(如享元模式)。 |
| 增加了对象数量 | 每个策略都是一个对象,增加了内存开销(但可通过享元模式缓解)。 |
4.3 与状态模式的区别
| 对比点 | 策略模式 | 状态模式 |
|---|---|---|
| 目的 | 算法/行为可互换 | 对象状态变化导致行为变化 |
| 切换方式 | 由客户端主动设置 | 由状态自身决定下一个状态 |
| 客户端感知 | 需要知道具体策略 | 无需关心状态转换 |
五、总结
策略模式是 Java 开发中非常实用的设计模式,尤其适合算法族频繁变化 或多种业务规则并存 的场景。它通过组合与多态将算法的定义和使用分离,极大提高了系统的扩展性和可维护性。在实际项目中,可以结合工厂模式、枚举、Spring 容器等进一步提升策略的获取和管理效率。当遇到大量 if-else 判断算法选择时,不妨考虑用策略模式重构。