• 策略模式详解+代码案例


    首先简单介绍策略模式

    该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

    策略模式的主要角色如下

    • 抽象策略(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 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 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登录,我们只需要添加对应的策略就可以,无需再改动业务层代码。

  • 相关阅读:
    【密评】商用密码应用安全性评估从业人员考核题库(一)
    静态HTML旅行主题网页设计与实现——联途旅游网服务平台网(39页)HTML+CSS+JavaScript
    有自动交易股票的软件么,怎么实现全自动交易?
    Java发送mail并更新日历信息及jar包冲突问题
    Docker部署Webvirtmgr管理kvm虚拟机
    已有项目与git建立连接、老项目搭建git管理
    每日一题 337. 打家劫舍 III
    Java多线程笔记
    2023江苏省领航杯(部分CRYPTO题目复现)
    技术创新驱动销售 植宗山茶油首登排行榜
  • 原文地址:https://blog.csdn.net/weixin_66196770/article/details/137258671