场景引入
策略模式主要解决的是if/else带来的一个代码臃肿问题。它的主要思想就是针对一个功能,有多种实现方式,并可以根据不同的条件去选择合适的方式。例如,通常一个平台会有多种登录方式,账号密码、手机号验证码、第三方平台等。这时,我们就可以使用策略模式,根据其登录类型来调用其对应的登录方法实现。
优点
- 避免多重条件判断
- 扩展性强
- 算法能够自由切换
具体实现
创建登录通用接口
/**
* @description: 登录通用接口
* @author: guod
* @date: 2022-06-27 09:55
*/
public interface TokenGranterStrategy {
/**
* 登录方法
* @param loginParam
* @return
*/
AuthUserInfo grant(LoginParams loginParam);
}
创建登录抽象类,主要用于实现一些通用方法
/**
* @description: 登录抽象类
* @author: guod
* @date: 2022-06-27 09:56
*/
@Slf4j
@Component
public abstract class AbstractTokenGranter implements TokenGranterStrategy{
/**
* 请求的公共校验
*/
protected void publicCheck() {
log.info("publicCheck()");
}
/**
* 查询账号并校验账号相关信息
* @param loginParams
* @return
*/
protected AccountVo checkAccount(LoginParams loginParams) {
log.info("checkAccount()");
return null;
}
/**
* 根据账号信息,生成token
* @param accountVo
* @return
*/
protected AuthUserInfo createToken(AccountVo accountVo) {
log.info("createToken()");
return null;
}
}
创建对应的实现类
账号密码登录:
/**
* @description: 密码登陆
* @author: guod
* @date: 2022-06-27 10:04
*/
@Slf4j
@Component
public class PasswordStrategy extends AbstractTokenGranter {
@Override
public AuthUserInfo grant(LoginParams loginParam) {
publicCheck();
AccountVo accountVo = checkAccount(loginParam);
createToken(accountVo);
log.info("PasswordGranter");
return null;
}
}
第三方平登录:
/**
* @description: 第三方平台登录
* @author: guod
* @date: 2022-06-27 10:06
*/
@Slf4j
@Component
public class OpenIdStrategy extends AbstractTokenGranter {
@Override
public AuthUserInfo grant(LoginParams loginParam) {
publicCheck();
AccountVo accountVo = checkAccount(loginParam);
createToken(accountVo);
log.info("OpenIdTokenGranter");
return null;
}
}
手机号登录:
/**
* @description: 手机号码登陆
* @author: guod
* @date: 2022-06-27 10:05
*/
@Slf4j
@Component
public class MobileStrategy extends AbstractTokenGranter {
@Override
public AuthUserInfo grant(LoginParams loginParam) {
publicCheck();
AccountVo accountVo = checkAccount(loginParam);
createToken(accountVo);
log.info("MobileTokenGranter");
return null;
}
}
创建对应的工厂类
/**
* @description:
* @author: guod
* @date: 2022-06-27 10:14
*/
@Component
public class TokenGranterHolder {
private final Map<String, TokenGranterStrategy> granterMap = new ConcurrentHashMap<>();
public TokenGranterHolder(Map<String, TokenGranterStrategy> granterMap) {
this.granterMap.putAll(granterMap);
}
/**
* 根据登陆类型获取具体的处理类
* @param grantType
* @return
*/
public TokenGranterStrategy getGranter(String grantType) {
TokenGranterStrategy tokenGranterStrategy = granterMap.get(grantType);
Optional.ofNullable(tokenGranterStrategy).orElseThrow(() -> new BizException("不存在该登陆类型"));
return tokenGranterStrategy;
}
}
登录类型枚举
/**
* @description: 登录类型枚举类
* @author: guod
* @date: 2022-06-27 10:09
*/
@AllArgsConstructor
public enum GrantTypeEnum {
PASSWORD("password", "passwordStrategy"),
MOBILE("mobile", "mobileStrategy"),
OPENID("openId", "openIdStrategy");
@Getter
private final String type;
@Getter
private final String value;
public static String getValueByType(String type) {
for (GrantTypeEnum grantTypeEnum : values()) {
if (grantTypeEnum.getType().equals(type)) {
return grantTypeEnum.getValue();
}
}
return null;
}
}
认证成功返回用户相关信息
/**
* @description: 认证成功返回用户相关信息
* @author: guod
* @date: 2022-06-27 10:09
*/
@Data
public class AuthUserInfo implements Serializable {
private static final long serialVersionUID = -1L;
/**
* 对应登录身份的id
*/
private Long identityId;
/**
* 令牌
*/
private String token;
/**
* 刷新令牌
*/
private String refreshToken;
/**
* 过期时间(秒)
*/
private long expire;
/**
* 到期时间
*/
private LocalDateTime expiration;
}
最后验证下
/**
* @description:
* @author: zyk
* @date: 2022-06-27 11:01
*/
@SpringBootTest
@Slf4j
public class StrategyTest {
@Autowired
private TokenGranterHolder tokenGranterHolder;
@Test
public void test() {
String grantType = GrantTypeEnum.getValueByType("password");
TokenGranterStrategy tokenGranterStrategy = tokenGranterHolder.getGranter(grantType);
AuthUserInfo authUserInfo = tokenGranterStrategy.grant(new LoginParams());
}
}