还在用if-else? 用策略模式干掉它

简介: 策略模式是一种**行为**设计模式,它将一组行为转换为对象, 并使其在原始上下文对象内部能够相互替换。大白话就是比如我写一个登录业务,目前需要满足能通过系统内、微信等平台进行登录,未来还有可能引入其他的平台,这个时候就可以采用策略模式,来让不同的平台的登录都有对应的策略路径。

策略模式(Strategy Pattern)

策略模式是一种行为设计模式,它将一组行为转换为对象, 并使其在原始上下文对象内部能够相互替换。大白话就是比如我写一个登录业务,目前需要满足能通过系统内、微信等平台进行登录,未来还有可能引入其他的平台,这个时候就可以采用策略模式,来让不同的平台的登录都有对应的策略路径。

image.png

此外对于不同类型的交易方式(信用卡、支付宝、微信),生成唯一ID的策略(UUID、雪花算法、Leaf算法)等,我们都可以先用策略模式对其进行行为包装,然后提供给外界进行调用。

一、 策略模式介绍

在策略模式中,主要有两个部分:

  • 表示各种策略的对象Strategy
  • 行为随着策略对象改变而改变的原始对象Context,它主要用于分发不同策略对象

注意,如果一个系统中的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。下面来看看对应的UML结构图:

image-20220413094838056

  • Stategy:抽象策略结构,定义不同策略需要执行的统一步骤和方法
  • ConcreteStrategy1、ConcreteStrategy2:实现抽象策略定义的接口,提供具体的算法实现
  • Context:上下文类,是外界获取不同策略的接口

二、策略模式应用

2.1 Java Comparator中的策略模式

java.util.comparator 中,comparator作为比较的接口,可以实现具体的比较策略。而java.util.Collections 中的sort(List<T> list, Comparator<? super T> c) 作为context类,执行不同的比较逻辑

image.png

可以做一个排序的demo来演示:

 public static void main(String[] args) {
   
        ArrayList<Integer> integers = new ArrayList<>();
        integers.add(1);
        integers.add(3);
        integers.add(5);
        integers.add(4);
        integers.add(2);
        for (Integer integer : integers) {
   
            System.out.print(integer);
        }
        System.out.println("顺序后~");
        Collections.sort(integers,new AscComparator());
        for (Integer integer : integers) {
   
            System.out.print(integer);
        }
        System.out.println("逆序后~");
        Collections.sort(integers,new DescComparator());
        for (Integer integer : integers) {
   
            System.out.print(integer);
        }
        InstantiationStrategy

    }

    static class DescComparator implements Comparator<Integer> {
   
        @Override
        public int compare(Integer o1, Integer o2) {
   
            return o2 - o1;
        }
    }
    static class AscComparator implements Comparator<Integer> {
   
        @Override
        public int compare(Integer o1, Integer o2) {
   
            return o1 - o2;
        }
    }

最后输出:

13542顺序后~
54321逆序后~
12345

2.2 Spring Bean实例化中的策略模式

其中InstantiationStrategy 作为实例化策略接口,AbstractAutowireCapableBeanFactory 作为上下文,创建策略并调用

image.png

三、 策略模式实战

3.1 生成不同的ID策略

就拿生成唯一ID业务来举例子,比如在雪花算法提出之前,我们一般使用的是UUID 来确认唯一ID。但是如果需要有序的生成ID,这个时候就要考虑一下其他的生成方法,比如雪花、Leaf等算法了。

可能刚开始我们是直接写一个类,在类里面调用UUID算法来生成,但是需要调用其他方法时,我们就必须在这个类里面用if-else等逻辑判断,然后再转换成另外的算法中。这样的做法和前面提到的工厂模式一样,会提高类之间的耦合度。所以我们可以使用策略模式将这些策略抽离出来,单独实现,防止后期若需要扩展带来的混乱。

首先,定义一个ID生成的接口IIdGenerator

public interface IIdGenerator {
   
    /**
     * 获取ID, 目前有三种实现方式
     * 1.雪花算法,主要用于生成单号
     * 2.日期算法,用于生成活动标号类,特性是生成数字串较短,但是指定时间内不能生成太多
     * 3.随机算法,用于生成策略ID
     * @return ID 返回ID
     */
    long nextId();
}

让不同生成ID策略实现该接口:

下面是雪花算法的具体实现 :

public class SnowFlake implements IIdGenerator {
   

    private Snowflake snowflake;

    @PostConstruct
    public void init() {
   
        //总共有5位,部署0~32台机器
        long workerId;
        try {
   
            workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
        } catch (Exception e) {
   
            workerId = NetUtil.getLocalhostStr().hashCode();
        }

        workerId = workerId >> 16 & 31;

        long dataCenterId = 1L;
        snowflake = IdUtil.createSnowflake(workerId, dataCenterId);
    }

    @Override
    public long nextId() {
   
        return snowflake.nextId();
    }
}

其次还要定义一个ID策略控制类IdContext ,通过外部不同的策略,利用统一的方法执行ID策略计算,如下所示:

@Configuration
public class IdContext {
   

    @Bean
    public Map<Constants.Ids, IIdGenerator> idGenerator(SnowFlake snowFlake, ShortCode shortCode, RandomNumeric randomNumeric) {
   
        Map<Constants.Ids, IIdGenerator> idGeneratorMap = new HashMap<>(8);
        idGeneratorMap.put(Constants.Ids.SnowFlake, snowFlake);
        idGeneratorMap.put(Constants.Ids.ShortCode, shortCode);
        idGeneratorMap.put(Constants.Ids.RandomNumeric, randomNumeric);
        return idGeneratorMap;
    }
}

所以在最后测试时,直接调用idGeneratorMap就可以实现不同策略服务的调用:

 @Test
 public void init() {
   
     logger.info("雪花算法策略,生成ID: {}", idGeneratorMap.get(Constants.Ids.SnowFlake).nextId());
     logger.info("日期算法策略,生成ID: {}", idGeneratorMap.get(Constants.Ids.ShortCode).nextId());
     logger.info("随机算法策略,生成ID: {}", idGeneratorMap.get(Constants.Ids.RandomNumeric).nextId());
 }
  1. 2 实现不同平台登录系统

正如前言提到的,在同样的登录过程中,需要实现不同平台的登录策略,这里就单列出微信登录系统的实现逻辑来展示策略模式:

  1. 登录接口

该部分提供给前端进行调用,通过前台传递不同的平台参数,来执行不同的登录策略:

public Result<JSONObject> login(@RequestBody LoginRequestModel loginRequest) {
   
    Result<JSONObject> result = new Result<JSONObject>();
    if (loginRequest.getThirdPlatform() == null) {
   
        result.error500("找不到该平台,请配置后再登录!");
        return result;
    } else {
   
        LoginStrategy loginStrategy = loginStrategyContext.getLoginStrategy(loginRequest.getThirdPlatform());
        result = loginStrategy.login(loginRequest);
        return result;
    }
}
  1. 登录策略context

该部分主要通过创建登录策略来进行调用, 这里是利用spring来将不同策略对象提前注入,方便管理和调用

@Component
public class LoginStrategyContext{
   

    private Map<String, LoginStrategy> strategies = new ConcurrentHashMap<>();

    /**
     * 将所有策略注入springBean中
     * @param strategies
     */
    public LoginStrategyContext(Map<String, LoginStrategy> strategies) {
   
        strategies.forEach(this.strategies::put);
    }
    public LoginStrategy getLoginStrategy(String strategyName) {
   
        LoginStrategy loginStrategy = strategies.get(strategyName);
        return loginStrategy;
    }

}
  1. 策略接口和具体实现

该部分完成登录策略接口和具体的登录实现策略

public interface LoginStrategy {
   
    /**
     * 处理具体登录逻辑
     * @return
     */
    Result<JSONObject> login(LoginRequestModel loginRequest);
}
public class WeChatLoginStrategy implements LoginStrategy {
   
    @Override
    public Result<JSONObject> login(LoginRequestModel loginRequest) {
   
        //处理具体的登录逻辑
    }
}

以上就是关于策略设计模式的内容,其实在日常业务逻辑中对于设计模式的使用,并不是非得一定要代码中有设计模式才行,简单的逻辑就用if-else即可。如果有复杂的业务逻辑,而且也符合对应的设计模式,这样使用模式才能真正提高代码的逻辑性和可扩展性。

目录
相关文章
|
6天前
|
设计模式 算法 PHP
php设计模式--策略模式(六)
php设计模式--策略模式(六)
13 0
|
6天前
|
设计模式 算法 前端开发
【面试题】什么是策略模式?用了策略模式之后,再也不用写那么多 if else 了,真香!
【面试题】什么是策略模式?用了策略模式之后,再也不用写那么多 if else 了,真香!
|
9月前
|
设计模式 前端开发 Java
意外发现,原来你不知道自己每天都在用门面模式
一般的电商平台都是整合众多的子系统聚合到一起形成一个大型的购物平台,一般情况下,有很多现成的功能都不是重新开发的,而是要去对接已有的各个子系统,这些子系统可能涉及积分系统、支付系统、物流系统的接口调用。如果所有的接口调用全部由前端发送网络请求去调用现有接口,一则会增加前端开发人员的难度,二则会增加一些网络请求,影响页面性能。此时就可以发挥门面模式的优势了。将所有现成的接口全部整合到一个类中,由后端提供统一的接口供前端调用,这样前端开发人员就不需要关心各接口的业务关系,只需要把精力集中在页面交互上。我们用代码来模拟一个积分兑换礼品的业务场景。
70 0
意外发现,原来你不知道自己每天都在用门面模式
|
6天前
|
算法
犯错总结--工厂模式和策略模式傻傻没分清
犯错总结--工厂模式和策略模式傻傻没分清
39 0
犯错总结--工厂模式和策略模式傻傻没分清
|
9月前
|
设计模式 算法
一文搞懂策略模式(优化策略模式完全消除if else)
一文搞懂策略模式(优化策略模式完全消除if else)
370 0
|
9月前
|
设计模式 算法 C++
2023-7-12-第十七式状态模式
2023-7-12-第十七式状态模式
47 0
阿里面试官:使用策略模式+工厂模式干掉代码中过多的if-else
过多if-else项目背景 如果一开始就知道现在的业务需要,大部分人都不会在代码里添加过多的if-else判断的,烂代码基本都是刚开始写代码时并没有太多的需求,随着期需求不断的修改增加,开发时间也较的紧张,代码往往都是怎么快速怎么写。当然多写一个if-else比使用各种设计模式肯定来的更快速了,这也就导致项目代码慢慢变得臃肿,难以维护的主要原因。在有空闲时间的情况下就可以给以前的代码做一次手术了。先看本次未优化前的代码:
|
设计模式 C++
用设计模式去掉没必要的状态变量 —— 状态模式
这是在UI开发中经常会遇到的场景:界面有两种状态,每一种状态下界面元素对应的操作都不同。比如在 offline 状态下点击大叉会直接退出应用,而在 login 状态下点击大叉会退出登录。 最简单直观的
69 0
|
设计模式 uml 微服务
面试官:谈谈简单工厂模式和策略模式的区别?我居然答不上来。。
面试官:谈谈简单工厂模式和策略模式的区别?我居然答不上来。。
106 0
面试官:谈谈简单工厂模式和策略模式的区别?我居然答不上来。。
|
算法
还在用繁琐的if..else,试试策略模式吧!
应该有不少码友遇到过充斥着if else的代码,面对这样的一团乱麻,简单粗暴地继续增量修改常常只会让复杂度越来越高,可读性越来越差。那么是时候重构了,花几分钟看看这篇文章, 说不定对你有一丢丢帮助。