首先简单介绍策略模式
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
策略模式的主要角色如下:
- 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
这时候策略模式其实就是接口定义规范+扩展不同的实现类进行方法重写来完成策略模式的扩展
案例
登录接口中可能会有多种登录方式:
- 用户名密码登录
- 短信验证码登录
- 微信登录
- QQ登录
- ....
此时业务层代码大量使用到了if...else,在后期阅读代码的时候会非常不友好,大量使用if...else性能也不高.
解决
我们采用策略模式+工厂模式可以完整的实现
(1)整体思路
改造之后,不在service中写业务逻辑,让service调用工厂,然后通过service传递不同的参数来获取不同的登录策略(登录方式)
(2)具体实现
抽象策略类:UserGranter
/** * 抽象策略类 */ public interface UserGranter{ /** * 获取数据 * @param loginReq 传入的参数 * @return map值 */ LoginResp login(LoginReq loginReq); }
具体的策略:AccountGranter、SmsGranter、WeChatGranter
/** * 策略:账号登录 **/ @Component public class AccountGranter implements UserGranter{ @Override public LoginResp login(LoginReq loginReq) { System.out.println("登录方式为账号登录" + loginReq); // TODO // 执行业务操作 return new LoginResp(); } } /** * 策略:短信登录 */ @Component public class SmsGranter implements UserGranter{ @Override public LoginResp login(LoginReq loginReq) { System.out.println("登录方式为短信登录" + loginReq); // TODO // 执行业务操作 return new LoginResp(); } } /** * 策略:微信登录 */ @Component public class WeChatGranter implements UserGranter{ @Override public LoginResp login(LoginReq loginReq) { System.out.println("登录方式为微信登录" + loginReq); // TODO // 执行业务操作 return new LoginResp(); } }
工程类:UserLoginFactory
下方的setApplicationContext方法是由于这个类继承了ApplicationContextAware上下文对象,这个方法会被spring自动调用,变相的说这里完成了对所有策略的集合map的初始化.
/** * 操作策略的上下文环境类 工具类 * 将策略整合起来 方便管理 */ @Component public class UserLoginFactory implements ApplicationContextAware { private static Map<String, UserGranter> granterPool = new ConcurrentHashMap<>(); @Autowired private LoginTypeConfig loginTypeConfig; /** * 从配置文件中读取策略信息存储到map中 * { * account:accountGranter, * sms:smsGranter, * we_chat:weChatGranter * } * * @param applicationContext * @throws BeansException * */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { loginTypeConfig.getTypes().forEach((k, y) -> { granterPool.put(k, (UserGranter) applicationContext.getBean(y)); }); } /** * 对外提供获取具体策略 * * @param grantType 用户的登录方式,需要跟配置文件中匹配 * @return 具体策略 */ public UserGranter getGranter(String grantType) { UserGranter tokenGranter = granterPool.get(grantType); return tokenGranter; } }
在application.yml文件中新增自定义配置,这里的key是前端传入的type值,value是IOC容器中对象类的名字,在上方的getGranter方法中会通过key获取到IOC中对应的对象
login: types: account: accountGranter sms: smsGranter we_chat: weChatGranter
新增读取数据配置类
Getter @Setter @Configuration @ConfigurationProperties(prefix = "login") public class LoginTypeConfig { private Map<String,String> types; }
改造service代码
@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); return loginResp; } LoginResp loginResp = granter.login(loginReq); return loginResp; } }
可以看到我们使用了设计模式之后,业务层的代码就清爽多了,如果后期有新的需求改动,比如加入了QQ登录,我们只需要添加对应的策略就可以,无需再改动业务层代码。
标签:loginReq,策略,登录,代码,UserGranter,案例,详解,LoginResp,public From: https://blog.csdn.net/weixin_66196770/article/details/137258671