Skip to content
HeZzz
Go back

策略模式

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户端。

模式结构

策略模式包含三个核心角色:

  1. 策略接口(Strategy):定义所有支持的算法的公共接口
  2. 具体策略(Concrete Strategy):实现了策略接口的具体算法类
  3. 环境上下文(Context):持有一个策略对象的引用,提供给客户端使用

应用场景

策略模式适用于以下场景:

完整代码示例:多方式登录系统

下面是一个完整的策略模式实现示例,展示了如何使用策略模式+工厂模式优化多方式登录系统:

1. 策略接口定义

public interface UserGranter {
    // 登录接口
    LoginResp login(LoginReq loginReq);
    
    // 获取登录类型
    LoginType getLoginType();
}

2. 具体策略实现

// 账号密码登录策略
@Component
public class AccountGranter implements UserGranter {
    @Autowired
    private UserService userService;
    
    @Override
    public LoginResp login(LoginReq loginReq) {
        LoginResp loginResp = new LoginResp();
        // 验证账号密码逻辑
        if ("admin".equals(loginReq.getUsername()) && "123456".equals(loginReq.getPassword())) {
            loginResp.setSuccess(true);
            loginResp.setMessage("账号密码登录成功");
        } else {
            loginResp.setSuccess(false);
            loginResp.setMessage("账号或密码错误");
        }
        return loginResp;
    }
    
    @Override
    public LoginType getLoginType() {
        return LoginType.PASSWORD;
    }
}

// 短信登录策略
@Component
public class SmsGranter implements UserGranter {
    @Override
    public LoginResp login(LoginReq loginReq) {
        LoginResp loginResp = new LoginResp();
        // 验证短信验证码逻辑
        if ("123456".equals(loginReq.getSmsCode())) {
            loginResp.setSuccess(true);
            loginResp.setMessage("短信登录成功");
        } else {
            loginResp.setSuccess(false);
            loginResp.setMessage("验证码错误");
        }
        return loginResp;
    }
    
    @Override
    public LoginType getLoginType() {
        return LoginType.SMS;
    }
}

// 微信登录策略
@Component
public class WeChatGranter implements UserGranter {
    @Override
    public LoginResp login(LoginReq loginReq) {
        LoginResp loginResp = new LoginResp();
        // 微信登录验证逻辑
        if ("wechat_token_123".equals(loginReq.getWechatToken())) {
            loginResp.setSuccess(true);
            loginResp.setMessage("微信登录成功");
        } else {
            loginResp.setSuccess(false);
            loginResp.setMessage("微信登录失败");
        }
        return loginResp;
    }
    
    @Override
    public LoginType getLoginType() {
        return LoginType.WECHAT;
    }
}

3. 登录类型枚举

public enum LoginType {
    PASSWORD("password", "账号密码登录"),
    SMS("sms", "短信登录"),
    WECHAT("wechat", "微信登录");
    
    private String code;
    private String description;
    
    LoginType(String code, String description) {
        this.code = code;
        this.description = description;
    }
    
    public String getCode() {
        return code;
    }
    
    public static LoginType getByCode(String code) {
        for (LoginType type : values()) {
            if (type.getCode().equals(code)) {
                return type;
            }
        }
        return null;
    }
}

4. 策略工厂

@Component
public class UserLoginFactory implements ApplicationContextAware {
    private Map<LoginType, UserGranter> granterPool = new HashMap<>();
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 获取所有UserGranter接口的实现类
        Map<String, UserGranter> granterMap = applicationContext.getBeansOfType(UserGranter.class);
        granterMap.forEach((key, granter) -> {
            granterPool.put(granter.getLoginType(), granter);
        });
    }
    
    public UserGranter getGranter(LoginType loginType) {
        return granterPool.get(loginType);
    }
    
    public UserGranter getGranter(String loginTypeCode) {
        LoginType loginType = LoginType.getByCode(loginTypeCode);
        return loginType != null ? granterPool.get(loginType) : null;
    }
}

5. 请求响应对象

// 登录请求
public class LoginReq {
    private String type; // 登录类型
    private String username;
    private String password;
    private String smsCode;
    private String wechatToken;
    
    // getters and setters
}

// 登录响应
public class LoginResp {
    private boolean success;
    private String message;
    private String token;
    
    // getters and setters
}

6. 服务层调用

@Service
public class UserService {
    @Autowired
    private UserLoginFactory factory;
    
    public LoginResp login(LoginReq loginReq) {
        // 获取对应的登录策略
        UserGranter granter = factory.getGranter(loginReq.getType());
        if (granter == null) {
            LoginResp loginResp = new LoginResp();
            loginResp.setSuccess(false);
            loginResp.setMessage("不支持的登录方式");
            return loginResp;
        }
        
        // 执行登录逻辑
        return granter.login(loginReq);
    }
}

策略模式的优点

  1. 开闭原则:无需修改上下文即可引入新策略
  2. 避免条件判断:消除了大量的条件判断语句
  3. 算法复用:可以在多个上下文中复用策略类
  4. 实现分离:将算法的实现与使用分离

策略模式的缺点

  1. 客户端必须了解策略差异:客户端需要选择合适的策略
  2. 增加对象数量:每个策略都是一个类,可能增加系统对象数量
  3. 通信开销:策略与上下文之间可能需要共享数据,增加通信开销

与其他模式的关系

总结

策略模式通过将算法封装到独立的策略类中,使得它们可以相互替换,让算法的变化独立于使用算法的客户端。这种模式特别适用于有多种算法实现相似功能,且需要在运行时动态选择算法的场景。

在实际开发中,策略模式常与工厂模式结合使用,通过工厂来管理和创建策略对象,进一步降低客户端与具体策略的耦合度,提高系统的灵活性和可维护性。


Share this post on:

上一篇
星火文脉智承:基于Vue.js和Node.js的中华文化数字内容制作平台
下一篇
Redisson