专栏首页极客起源小白写了一堆if-else,大神实在看不下去了,竟然用策略模式直接摆平了!

小白写了一堆if-else,大神实在看不下去了,竟然用策略模式直接摆平了!

这里涉及到一个关键词:策略模式,那么到底什么是策略模式呢?本文就来好好给大家讲讲策略模式,大家可以带着如下几个问题来阅读本文:

1. 如何通过策略模式优化业务逻辑代码(可以根据自己从事的工作思考)

2. 使用策略模式优化if-else,会不会带来什么副作用呢?

3. 实现策略模式是否有更好的方式呢?

1. 策略模式如何优化代码解构

要会带这个问题,需要先弄清楚策略模式的定义,首先来看策略模式的教科书定义:

策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。策略模式是一种对象行为型模式。

这里的算法其实就是业务逻辑,为了更形象,干脆将其理解为一个函数。其实这个定义的基本意思就是说,根据策略选择要执行的函数,而每一个策略都会有一个标识,可以称为key。而当前策略要执行的函数,可以称为value,其实就是使用key寻找value,然后执行vlaue的过程。也就是说,一个key对应一个value,从这层意思上理解,就是if-else要干的事。

那么策略模式到底优化了什么呢?

其实策略模式的核心思想与 if else如出一辙,根据不同的key动态的找到不同的业务逻辑(要执行的函数)。那它就只是如此吗?

实际上,我们口中的策略模式其实就是在代码结构上调整,用接口+实现类+分派逻辑来使代码结构可维护性更好。

一般教科书上讲到接口与实现类就结束了,其他博客上会带上提及分派逻辑。这里就不啰嗦了。现在总结一下:即使用了策略模式,所有的业务逻辑一样都少不了,改写的仍然需要写。到逻辑分派的时候,还是变相的if-else。而策略模式的优化点是抽象了出了接口,将业务逻辑封装成一个一个的实现类,任意地替换。在复杂场景(业务逻辑较多)时比直接 使用if-else 更好维护。

2. 使用策略模式优化if-else,会不会带来什么副作用呢?

我估计肯定会有很多同学这么想:我的业务逻辑就几行,你给我整一大堆类定义?有必要这么麻烦吗?我看具体的业务逻辑还需要去不同的类中,简单点不香吗!

其实这里的不满也正是策略模式的缺点:

(1) 策略类会增多

(2) 业务逻辑分散到各个实现类中,而且没有一个地方可以俯视整个业务逻辑

针对传统策略模式的缺点,在这分享一个实现思路,这个思路已经帮我们团队解决了多个复杂if else的业务场景,理解上比较容易,在技术上要使用到Java8的特性:Map与函数式接口。

废话少说,直接看代码:

先看两个方法:

1. processResult() :使用if-else的方法

2. processPolicyResult():使用策略模式的方法。在该方法中事先在Map中定义好了“判断条件”与“业务逻辑”的映射关系,具体看代码吧!

/**
 * 策略模式演示类
 */
public class MyService {

    /**
     * 使用if-else的解决方案
     */
    public String processResult(String key) {
        if ("checkvalue1".equals(key)) {
            return "business logic1";   
        } else if ("checkvalue2".equals(key)) {
            return "business logic2";
        }else if ("checkvalue3".equals(key)) {
            return "business logic3";
        }else if ("checkvalue4".equals(key)) {
            return "business logic4";
        }else if ("checkvalue5".equals(key)) {
            return "business logic5";
        }else if ("checkvalue6".equals(key)) {
            return "business logic6";
        }else if ("checkvalue7".equals(key)) {
            return "business logic7";
        }else if ("checkvalue8".equals(key)) {
            return "business logic8";
        }else if ("checkvalue9".equals(key)) {
            return "business logic9";
        }
        return "error;
    }

    /**
     * 用于业务逻辑分派Map
     * Function为函数式接口,下面代码中 Function<String, String> 的含义是接收一个String类型的变量,返回一个String类型的结果
     */
    private Map<String, Function<String, String>> myDispatcher = new HashMap<>();

    /**
     * 使用策略模式的方法
     */
  
    public void  policyInit() {
        myDispatcher.put("checkvalue1", key -> String.format("business logic1 for %s1", key));
        myDispatcher.put("checkvalue2", key -> String.format("business logic2 for %s2", key));
        myDispatcher.put("checkvalue3", key -> String.format("business logic3 for %s3", key));
        myDispatcher.put("checkvalue4", key -> String.format("business logic4 for %s4", key));
        myDispatcher.put("checkvalue5", key -> String.format("business logic5 for %s5", key));
        myDispatcher.put("checkvalue6", key -> String.format("business logic6 for %s6", key));
        myDispatcher.put("checkvalue7", key -> String.format("business logic7 for %s7", key));
        myDispatcher.put("checkvalue8", key -> String.format("business logic8 for %s8", key));
        myDispatcher.put("checkvalue9", key -> String.format("business logic9 for %s9", key));
    }

    public String processPolicyResult(String key) {
        //从逻辑分派Dispatcher中获得业务逻辑代码,result变量是一个lambda表达式
        Function<String, String> result = myDispatcher.get(key);
        if (result != null) {
            //执行这段表达式获得String类型的结果
            return result.apply(key);
        }
        return "error";
    }
}

下面是调用代码:

public class RunPolicy {
    private MyService myService;

    public String test(String key) {
        return myService.processPolicyResult(order);
    }
}

从这段代码中可以看到很多好处,例如:

(1)在policyInit()方法中直观地看到“判断条件”与“业务逻辑”的映射关系;

(2)不需要单独定义接口与实现类,直接使用现有的函数式接口即可;

3. 策略模式在真实场景中的应用

可能有的同学会说,我的条件判断可能非常复杂,而前面的案例只有一个条件判断(key),其实这就和数据库中通过单个字段作为索引,还是使用复合索引(多个字段共同组成索引)的问题。我们也可以用复合条件来实现策略模式,上代码:

/**
 * 策略模式类
 */
public class PolicyService {

    private Map<String, Function<String, String>> myDispatcherMulti = new HashMap<>();

    /**
     * 初始化 业务逻辑分派Map 其中value 存放的是 lambda表达式
     */
    @PostConstruct
    public void dispatcherMuitInit() {
        myDispatcherMulti.put("key_order1", key -> String.format("business logic1 for %s", key));
        myDispatcherMulti.put("key_order2_order3", key -> String.format("business logic2 for %s", key));
        myDispatcherMulti.put("key_order1_order2_order3", key -> String.format("business logic3 for %s", key));
    }

    public String processMuti(String key, int level) {
        //根据level获取不同的key
        String dKey = getDispatcherKey(key, level);

        Function<String, String> result = myDispatcherMuti.get(dKey);
        if (result != null) {
            //执行这段表达式获得String类型的结果
            return result.apply(key);
        }
        return "error";
    }

    /**
     * 根据level生成不同层次的key
     */
    private String getDispatcherKey(String key, int level) {
        StringBuilder k = new StringBuilder("key");
        for (int i = 1; i <= level; i++) {
            k.append("_" + order + i);
        }
        return k.toString();
    }
}

/**
 * 测试代码
 */
public class  TestPolicyMulti {


    private PolicyService policyService;

    public String test(String key, int level) {
        return policyService.processMuti(key, level);
    }
}
在这段代码中,key是满足一定规则的复合条件,只要设计好key的生成规则就一切ok!
可能还会有很多同学问,我的业务逻辑有很多行,在dispatcherMuitInit()方法的Map中直接写不会很长吗?直接写当然长了,我们可以抽象出一个服务类专门放业务逻辑,然后在定义中调用它就可以了,代码如下:
/**
 * 专门放业务逻辑的服务类
 */
public class ServiceUnit {

    public String task1(String key) {
        return "业务逻辑1";
    }
    public String task2(String key) {
        return "业务逻辑2";
    }
     public String task3(String key) {
        return "业务逻辑3";
    }
     public String task4(String key) {
        return "业务逻辑4";
    }
}
/**
 * 使用策略模式的类
 */
public class PolicyService {
    private ServiceUnit serviceUnit;

    private Map<String, Function<String, String>> myDispatcher = new HashMap<>();

    /**
     * 初始化规则映射
     */
    public void dispatcherInit() {
        myDispatcher.put("key_order1", key -> serviceUnit.task1(key));
        myDispatcher.put("key_order1_order2", key -> serviceUnit.task2(key)));
        myDispatcher.put("key_order1_order2_order3", key -> serviceUnit.task3(key));
        myDispatcher.put("key_order1_order2_order3_order4", key -> serviceUnit.task4(key));
    }

    public String process(String key, int level) {
        // 根据level生成对应的key
        String dKey = getDispatcherKey(key, level);

        Function<String, String> result = myDispatcher.get(dKey);
        if (result != null) {
            //执行这段表达式获得String类型的结果
            return result.apply(order);
        }
        return "error";
    }

    /**
     * 根据level生成对应的key
     */
    private String getDispatcherKey(String key, int level) {
        StringBuilder k = new StringBuilder("key");
        for (int i = 1; i <= level; i++) {
            k.append("_" + order + i);
        }
        return k.toString();
    }
}

在这段代码中,key是满足一定规则的复合条件,只要设计好key的生成规则就一切ok!

可能还会有很多同学问,我的业务逻辑有很多行,在dispatcherMuitInit()方法的Map中直接写不会很长吗?直接写当然长了,我们可以抽象出一个服务类专门放业务逻辑,然后在定义中调用它就可以了,代码如下:

/**
 * 专门放业务逻辑的服务类
 */
public class ServiceUnit {

    public String task1(String key) {
        return "业务逻辑1";
    }
    public String task2(String key) {
        return "业务逻辑2";
    }
     public String task3(String key) {
        return "业务逻辑3";
    }
     public String task4(String key) {
        return "业务逻辑4";
    }
}
/**
 * 使用策略模式的类
 */
public class PolicyService {
    private ServiceUnit serviceUnit;

    private Map<String, Function<String, String>> myDispatcher = new HashMap<>();

    /**
     * 初始化规则映射
     */
    public void dispatcherInit() {
        myDispatcher.put("key_order1", key -> serviceUnit.task1(key));
        myDispatcher.put("key_order1_order2", key -> serviceUnit.task2(key)));
        myDispatcher.put("key_order1_order2_order3", key -> serviceUnit.task3(key));
        myDispatcher.put("key_order1_order2_order3_order4", key -> serviceUnit.task4(key));
    }

    public String process(String key, int level) {
        // 根据level生成对应的key
        String dKey = getDispatcherKey(key, level);

        Function<String, String> result = myDispatcher.get(dKey);
        if (result != null) {
            //执行这段表达式获得String类型的结果
            return result.apply(order);
        }
        return "error";
    }

    /**
     * 根据level生成对应的key
     */
    private String getDispatcherKey(String key, int level) {
        StringBuilder k = new StringBuilder("key");
        for (int i = 1; i <= level; i++) {
            k.append("_" + order + i);
        }
        return k.toString();
    }
}

总结:

1. 如何通过策略模式优化业务逻辑代码(可以根据自己从事的工作思考)

抽象了出了接口,将业务逻辑封装成的实现类,任意地替换。在复杂场景(业务逻辑较多)时比直接 使用if-else 更好维护。

2. 使用策略模式优化if-else,会不会带来什么副作用呢?

其实使用策略模式大多数时候会带来很多好处,不过也会有一些不足的:

(1)策略类比较多;

(2)业务逻辑分散到各个实现类中,而且没有一个地方可以俯览整个业务逻辑;

3. 实现策略模式是否有更好的方式呢?

可以用函数函数式接口实现业务逻辑,这样可以更直观观察策略和执行逻辑的关系。

本文分享自微信公众号 - 极客起源(geekculture),作者:geekori

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-07-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python面试算法:绘制谢尔宾斯基三角形

    以上就是一个6级的谢尔宾斯基三角形。也就是三角形有6个尺寸,最大的是最外面的一个三角形,最大。再下一个级别的就是里面的4个三角形(中间的是粉色的)。如下图就是左...

    蒙娜丽宁
  • Python编程思想(20):变量作用域

    在程序中定义一个变量时,这个变量是有作用范围的。变量的作用范围被称为它的作用域。根据定义变量的位置,变量分为如下两种:

    蒙娜丽宁
  • 小程序开发实战(7):Button组件详解

    下面的布局代码详细描述了上述大多数属性的用法(除了form-type外)。在这段布局文件中,放置了6个button组件,其中前三个演示了3种按钮类型:defau...

    蒙娜丽宁
  • java:java.util.Properties的便利性封装

    版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net...

    用户1148648
  • java如何指定外部的配置文件

    工作当中很多时候都希望可以把配置文件外放,这样的话就可以做到配置与业务分离,其实有很多种放式,比如xml,properties,这里就说一下如何用propert...

    shengjk1
  • Java 枚举 String-String

    week
  • Spring Data Redis 最佳实践!

    使用该注解的方法当缓存存在时,会从缓存中获取数据而不执行方法,当缓存不存在时,会执行方法并把返回结果存入缓存中。一般使用在查询方法上,可以设置如下属性:

    macrozheng
  • spring+redis的集成,redis做缓存

           Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。我们都知...

    别先生
  • Java Comparable 与 Comparator

    在收集对象之后,对象的排序是经常需要用到的操作。但我们不需要亲自实现各种排序算法,java.util.Collections提供了sort方法,List作为一种...

    desperate633
  • 分布式锁-redis实现

    爱撒谎的男孩

扫码关注云+社区

领取腾讯云代金券