你还在大篇幅的使用if…else吗?
举个例子:比如你们有一个订单系统,用户在平时下单和在双11的时候下单的时候逻辑是不一样的,可能双11下单就涉及到一些优惠之类的,这个时候你怎么做,应该有好多同学是这样做的,前端传一个参数来区分普通下单和双11下单,后台用if else来判断两个分支来处理逻辑,那这样好像也没啥问题,但是后面到双12了,老板说双12优惠力度又不一样了,你又得加一个else ,然后还需要修改之前已经测试没问题的代码, 这样你这个代码块还需要重新测试而且整体的代码简洁度也不美观了
那有什么最优的办法呢?那就是使用策略模式
本篇文章将通过策略模式的概念和优缺点以及几个完整的示例来讲解如何在工作和学习当中将策略模式融入的你的业务当中
策略模式是一种设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。这种模式的主要目的是解决在有多种算法相似的情况下,使用“if…else”所带来的复杂和难以维护的问题。
策略模式的优点有:
总之,策略模式是一种通过封装算法和行为来简化复杂系统设计的模式,它允许在运行时根据需要动态地选择不同的策略实现。
策略模式适用于以下场景:
有些同学经常把策略模式和工厂模式弄混,那我们也来看看策略模式和工厂模式的区别:
总结来说,工厂模式和策略模式虽然相似,但它们的设计理念和适用场景有所不同。工厂模式注重创建对象,而策略模式注重行为的封装和算法的独立性。因此,在使用时需要根据具体需求选择合适的模式。
下面以一个简单的代码示例来演示策略模式:
// 定义一个接口
public interface Strategy {
void execute();
}
// 两个实现类分别实现这个接口
public class StrategyA implements Strategy {
@Override
public void execute() {
// 第一段逻辑
System.out.println("执行第一段逻辑");
}
}
public class StrategyB implements Strategy {
@Override
public void execute() {
// 第二段逻辑
System.out.println("执行第二段逻辑");
}
}
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
strategy.execute();
}
}
public class Main {
public static void main(String[] args) {
Context context = new Context(new StrategyA()); // 设置执行第一段逻辑的策略
context.executeStrategy(); // 执行策略,输出 "执行第一段逻辑"
context.setStrategy(new StrategyB()); // 更换为执行第二段逻辑的策略
context.executeStrategy(); // 执行策略,输出 "执行第二段逻辑"
}
}
在上面的示例中,我们定义了一个Strategy
接口,其中包含一个execute
方法,用于执行相应的逻辑。然后,我们创建了两个实现了Strategy
接口的类StrategyA
和StrategyB
,分别表示第一段逻辑和第二段逻辑。
我们还定义了一个Context
类,它持有一个Strategy
对象,并提供了一个executeStrategy
方法来执行相应的策略。客户端代码可以通过设置不同的策略对象来决定执行哪一段逻辑。在示例中,我们创建了一个Context
对象,并使用StrategyA
作为初始策略来执行第一段逻辑。然后,我们通过调用setStrategy
方法更换为StrategyB
来执行第二段逻辑。
相信好多同学光看上面的代码可能还不知道怎么将策略模式应用到自己的代码当中,那下面就给出一个个示例(本人实际上过生产的项目)
ps: 以下代码都是我模拟的生产代码(因为生产的不能公开哦)
登录认证想必大家都不陌生,那我们的项目可能就会对应几种认证方式:可能会是账号密码、验证码、扫码等等
下面我就实现两种认证方式来演示策略模式:
/**
* 统一认证接口
*
* @author jiagang
*/
public interface ITokenGranter {
/**
* 用户认证
*
* @param tokenParameter 授权参数
* @return UserInfo
*/
UserInfo grant(TokenParameter tokenParameter);
}
创建两个类分别实现ITokenGranter接口
账号密码实现:
@Component
@AllArgsConstructor
public class PasswordTokenGranter implements ITokenGranter {
public static final String GRANT_TYPE = "password";
private IUserService userClient;
@Override
public UserInfo grant(TokenParameter tokenParameter) {
// 下面逻辑简单模拟了就
String account = tokenParameter.getAccount();
String password = tokenParameter.getPassword();
// 通过账号和密码(解密后)来查询用户 -- userClient为调用用户服务的能力
return userClient.userInfo(account, DigestUtil.encrypt(password));
}
}
验证码实现:
@Component
@AllArgsConstructor
public class CaptchaTokenGranter implements ITokenGranter {
public static final String GRANT_TYPE = "captcha";
private IUserService userClient;
private RedisUtil redisUtil;
@Override
public UserInfo grant(TokenParameter tokenParameter) {
HttpServletRequest request = WebUtil.getRequest();
String key = request.getHeader(TokenUtil.CAPTCHA_HEADER_KEY);
String code = request.getHeader(TokenUtil.CAPTCHA_HEADER_CODE);
// 获取验证码
String redisCode = String.valueOf(redisUtil.get(CacheNames.CAPTCHA_KEY + key));
// 判断验证码
if (code == null || !StringUtil.equalsIgnoreCase(redisCode, code)) {
throw new ServiceException(TokenUtil.CAPTCHA_NOT_CORRECT);
}
// 下面逻辑简单模拟了就
String account = tokenParameter.getAccount();
String password = tokenParameter.getPassword();
// 通过账号和密码(解密后)来查询用户 -- userClient为调用用户服务的能力
return userClient.userInfo(account, DigestUtil.encrypt(password));
}
}
这个类的作用是通过授权方式(grantType)来确定使用哪个实现类
@AllArgsConstructor
public class TokenGranterBuilder {
/**
* TokenGranter缓存池
*/
private static final Map<String, ITokenGranter> GRANTER_POOL = new ConcurrentHashMap<>();
static {
GRANTER_POOL.put(PasswordTokenGranter.GRANT_TYPE, SpringUtil.getBean(PasswordTokenGranter.class));
GRANTER_POOL.put(CaptchaTokenGranter.GRANT_TYPE, SpringUtil.getBean(CaptchaTokenGranter.class));
}
/**
* 获取TokenGranter
*
* @param grantType 授权类型
* @return ITokenGranter
*/
public static ITokenGranter getGranter(String grantType) {
// password 为默认授权类型
ITokenGranter tokenGranter = GRANTER_POOL.get(toStr(grantType, PasswordTokenGranter.GRANT_TYPE));
if (tokenGranter == null) {
throw new SecureException("no grantType was found");
} else {
return tokenGranter;
}
}
public static String toStr(Object str, String defaultValue) {
return null == str ? defaultValue : String.valueOf(str);
}
}
@RestController
@AllArgsConstructor
@RequestMapping("/auth")
public class AuthController {
private IAuthService authService;
private RedisUtil redisUtil;
/**
* 登陆认证
*/
@PostMapping("login")
public R<UserInfo> token(@RequestBody TokenParameter tokenParameter) {
// 通过类型获取不同实现类的认证
ITokenGranter granter = TokenGranterBuilder.getGranter(tokenParameter.getGrantType());
// 调用接口
UserInfo userInfo = granter.grant(tokenParameter);
if (userInfo == null || userInfo.getUser() == null || userInfo.getUser().getId() == null) {
return R.fail(TokenUtil.USER_NOT_FOUND);
}
return R.data(userInfo);
}
}
内容结束啦,最后送大家一句话 白驹过隙,沧海桑田
文章持续更新,可以关注下方公众号或者微信搜一搜「 最后一支迷迭香 」获取项目源码,第一时间阅读,获取更完整的链路资料。