一文搞懂策略模式(优化策略模式完全消除if else)

简介: 一文搞懂策略模式(优化策略模式完全消除if else)

前言

策略模式是设计模式里面比较简单的设计模式,其特点简单又实用,是我最喜欢的模式之一。当初学策略模式是因为听说策略模式可以用来消除 if else语句。

多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if…else 语句、switch…case 语句。

一、策略模式介绍

在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。

在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。


如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。


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


20210519203202934.png

策略模式有三个组成角色:

抽象策略(Strategy)类

具体策略(Concrete Strategy)类

环境(Context)类

这三个角色贯穿策略模式的整个设计思想,大家要牢牢记住

策略模式有如下应用场景:

策略模式的优点和缺点我直接用表格方式给大家列举:

image.png

二、策略模式应用(消除 if else)

策略模式的简单应用

我们先看一段代码,看代码之前回想下我前面说的三个角色,把这三个角色代入到代码中,思考这三个角色中在代码的作用:

public class StrategyPattern {
    public static void main(String[] args) {
        Context c = new Context();
        Strategy s = new ConcreteStrategyA();
        c.setStrategy(s);
        c.strategyMethod();
        System.out.println("-----------------");
        s = new ConcreteStrategyB();
        c.setStrategy(s);
        c.strategyMethod();
    }
}
//抽象策略类
interface Strategy {
    public void strategyMethod();    //策略方法
}
//具体策略类A
class ConcreteStrategyA implements Strategy {
    public void strategyMethod() {
        System.out.println("具体策略A的策略方法被访问!");
    }
}
//具体策略类B
class ConcreteStrategyB implements Strategy {
    public void strategyMethod() {
        System.out.println("具体策略B的策略方法被访问!");
    }
}
//环境类
class Context {
    private Strategy strategy;
    public Strategy getStrategy() {
        return strategy;
    }
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    public void strategyMethod() {
        strategy.strategyMethod();
    }
}

大白话:其实说的就是有一个接口(抽象接口),他有2个实现类A和B(具体策略)。然后有一个环境类Context。这个接口是Context的成员变量,然后根据外在环境来选择是用A和B。


仔细想想,这种设计思想,是不是跟if else语句非常的像!,也是根据条件来选择,执行哪种具体策略。


20210519205141685.png

不多BB,我们来试试用策略模式改写if else

传统if else

    if{
        // 逻辑1
        ......
    } else {
        // 逻辑2
        ......
    }

用策略模式改写

Context c = new Context();
    if(conditions){
        // 逻辑1
        Strategy s = new ConcreteStrategyA();
        c.setStrategy(s);
        c.strategyMethod();
    } else {
        // 逻辑2
        Strategy s = new ConcreteStrategyB();
        c.setStrategy(s);
        c.strategyMethod();
    }
}

20210519211313306.png

emmm。优化了,但还没完全优化,这种写法依然还没脱离if else结构。


小结一下,即使用了策略模式,你该写的业务逻辑照常写,到逻辑分派的时候,还是变相的if else。而它的优化点是抽象了出了接口,将业务逻辑封装成一个一个的实现类,任意地替换。在复杂场景(业务逻辑较多)时比直接 if else 来的好维护些。

三、如何对策略模式进行优化

大家仔细想想,针对上述写法其实有2个痛点

1.具体策略类会过多

2.还无法彻底消除if else

第一个问题我们其实可以这样解决,把抽象策略和具体策略放在一个枚举类里

public enum Strategy {
    A{
        @Override
        public void exe() {
            System.out.println("执行具体策略A");
        }
    },
    B{
        @Override
        public  void exe() {
            System.out.println("执行具体策略B");
        }
    };
    public abstract void exe();
}

方法 exe() 相当于抽象策略,而A和B就相当于实现了抽象策略的具体策略
这样就只需要一个枚举类就可以解决具体策略类过多的问题,完美地解决了痛点~

再来看第二个痛点。彻底消除if else。

对了!直接用Map不就行了吗,Map<条件,具体策略>

预先put进去条件,需要的时候get不就行了吗

所以,解决之道就是 枚举类+Map

完整代码如下

public enum Strategy {
    A{
        @Override
        public void exe() {
            System.out.println("执行具体策略A");
        }
    },
    B{
        @Override
        public  void exe() {
            System.out.println("执行具体策略B");
        }
    };
    public abstract void exe();
}
public class Test {
    public static void main(String[] args) {
        Map<String, Strategy> map=new LinkedHashMap<>();
        map.put("A",Strategy.A);
        map.put("B",Strategy.B);
        String str="A";
        map.get(str).exe();
    }
}

这样的话即没有过多的具体策略类,也完全消除了if else

总结

写代码时总会出很多的if…else,或者case。如果在一个条件语句中又包含了多个条件语句就会使得代码变得臃肿,维护的成本也会加大,而策略模式就能较好的解决这个问题

先介绍了下策略模式,讲明了应用场景和优缺点,引出了策略模式的三大角色:

抽象策略;具体策略;环境

然后讲解了下策略模式的应用,普通写法还无法完全消除if else。

优化→枚举类+Map

来解决策略模式,具体策略类过多和无法完全消除if else的痛点。

以上就是本篇文章 关于 策略模式 的内容

骚等别划走,恰饭时间~

目录
相关文章
|
消息中间件 NoSQL Java
Redis监听Key的过期事件
在redis.conf配置文件中有个配置项:notify-keyspace-events " " ,默认是没有key的过期监听的,我们需要将其开启
2147 0
|
消息中间件 Java 物联网
一文搞懂MQTT,如何在SpringBoot中使用MQTT实现消息的订阅和发布
之前介绍了RabbitMQ以及如何在SpringBoot项目中整合使用RabbitMQ,看过的朋友都说写的比较详细,希望再总结一下目前比较流行的MQTT。所以接下来,就来介绍什么MQTT?它在IoT中有着怎样的作用?如何在项目中使用MQTT?
18909 63
一文搞懂MQTT,如何在SpringBoot中使用MQTT实现消息的订阅和发布
|
SQL XML 安全
mybatis批量更新数据三种方法效率对比【Mysql】
mybatis批量更新数据三种方法效率对比【Mysql】
4656 0
mybatis批量更新数据三种方法效率对比【Mysql】
|
4月前
|
消息中间件 架构师 Java
美团面试:对比分析 RocketMQ、Kafka、RabbitMQ 三大MQ常见问题?
美团面试:对比分析 RocketMQ、Kafka、RabbitMQ 三大MQ常见问题?
美团面试:对比分析 RocketMQ、Kafka、RabbitMQ 三大MQ常见问题?
|
存储 关系型数据库 MySQL
ES的全文索引和MySQL的全文索引有什么区别?如何选择?
【8月更文挑战第26天】ES的全文索引和MySQL的全文索引有什么区别?如何选择?
1311 5
|
8月前
|
缓存 监控 数据处理
Flink 四大基石之窗口(Window)使用详解
在流处理场景中,窗口(Window)用于将无限数据流切分成有限大小的“块”,以便进行计算。Flink 提供了多种窗口类型,如时间窗口(滚动、滑动、会话)和计数窗口,通过窗口大小、滑动步长和偏移量等属性控制数据切分。窗口函数包括增量聚合函数、全窗口函数和ProcessWindowFunction,支持灵活的数据处理。应用案例展示了如何使用窗口进行实时流量统计和电商销售分析。
1757 28
|
11月前
|
消息中间件 存储 算法
RocketMQ核心知识点整理,收藏再看!
RocketMQ核心知识点整理,收藏再看!
1057 0
RocketMQ核心知识点整理,收藏再看!
|
存储 SQL 算法
搞定了 6 种分布式ID,分库分表哪个适合做主键?
在《ShardingSphere5.x分库分表原理与实战》系列的第七篇文章中,作者探讨了分布式ID在分库分表中的重要性,以及如何利用`ShardingSphere-jdbc`的多种主键生成策略。文章介绍了`UUID`、`NanoID`、自定义雪花算法和`CosId`等策略的优缺点,并警告不要在SQL中手动拼接主键字段。此外,文章还展示了如何配置这些策略,并提醒读者`CosId`在5.2.0版本可能不可用。最后,文章讨论了如何自定义分布式主键生成算法,并强调选择策略时要考虑全局唯一性、性能和易用性。
1302 122
|
Java 应用服务中间件 API
如何在Java中实现WebSocket?
如何在Java中实现WebSocket?
|
存储 运维 NoSQL
如何撰写好的技术方案设计-真实案例干货分享
如何撰写好的技术方案设计-真实案例干货分享
1953 0

热门文章

最新文章