首页 > 其他分享 >策略模式详解+代码案例

策略模式详解+代码案例

时间:2024-04-02 16:31:45浏览次数:18  
标签:loginReq 策略 登录 代码 UserGranter 案例 详解 LoginResp public

首先简单介绍策略模式

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

策略模式的主要角色如下

  • 抽象策略(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

相关文章

  • 类加载过程详解
    1、加载通过类的全名,获取类的二进制数据流。解析类的二进制数据流为方法区内的数据结构(Java类模型) 创建java.lang.Class类的实例,表示该类型。作为方法区这个类的各种数据的访问入口 2、验证 验证类是否符合JVM规范,安全性检查 文件格式验证:是否符合Class文件的规范 元......
  • 14天【代码随想录算法训练营34期】 第六章 二叉树part01(● 理论基础 ● 递归遍历 ●
    理论基础种类满二叉树:k是深度,node数为2^k-1完全二叉树:二叉树底部是从左向右持续的二叉搜索树:左边节点都小于中间节点,右边节点都大于中间节点平衡二叉树AVL:左边和右边高度相差不超过1存储方式链式存储:leftchildptr,rightchildptr线式存储:字符数组保存,2i+1是左孩......
  • 面向对象14:static关键字详解
    ackagecom.oop.demo07;publicclassStudent{//staticprivatestaticintage;//静态变量多线程里会用到privatedoublescore;//非静态变量publicstaticvoidmain(String[]args){Students1=newStudent();System.out.p......
  • 多线程的案例
    目录1.单例模式1.1饿汉模式1.2懒汉模式-单线程版1.3懒汉模式-多线程版1.3懒汉模式-多线程版(改进)2.阻塞队列2.1阻塞队列是什么2.2生产者消费模型2.3标准库中的阻塞队列2.4阻塞队列实现3.定时器3.1定时器是什么3.2标准库中的定时器3.3实现定时器4.线程池4.1线......
  • HTML设置定时执行代码 JavaScript 计时事件
    1、https://www.runoob.com/js/js-timing.htmlJavaScript一个设定的时间间隔之后来执行代码我们称之为计时事件JavaScript计时事件通过使用JavaScript,我们有能力做到在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行。我们称之为计时事件。在JavaScript......
  • 层次式架构案例
                  ......
  • ES6 reduce方法:示例与详解、应用场景
    还是大剑师兰特:曾是美国某知名大学计算机专业研究生,现为航空航海领域高级前端工程师;CSDN知名博主,GIS领域优质创作者,深耕openlayers、leaflet、mapbox、cesium,canvas,webgl,echarts等技术开发,欢迎加底部微信(gis-dajianshi),一起交流。No.内容链接1Openlayers【入门教程】-......
  • 一步到位!快速精通Git工作流及实战技巧详解
    Git是一个分布式版本控制系统。1、git的应用场景1.备份小明负责的模块就要完成了,就在即将release之前的一瞬间,电脑突然蓝屏。硬盘光荣牺牲!几个月来的努力付之东流。场景二:代码还原这个项目中需要一个很复杂的功能,老王摸索了一个星期终于有眉目了,可是这被改得面目全非的......
  • STM32F103系列IIC通讯代码
    CH1--SDA;CH2--SCL。 #include"iic.h"voidIIC_GPIO_Init(void){GPIO_InitTypeDefGPIO_InitStruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin......
  • golang中GORM使用 many2many 多对多关联查询-详细案例
    表结构和数据user表CREATETABLE`user`(`id`bigint(20)NOTNULL,`user_key`bigint(20)NOTNULL,`account`char(32)NOTNULL)ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;为了测试将user_key和id写入同样的值数据:+----+----------+---------+|id|user_k......