如何用策略模式,优化你代码里的的if-else?

news2025/1/8 6:14:17

最近有一个学妹在跟我沟通如何有效的去避免代码中一长串的if else判断或者switch条件判断?针对更多的回答就是合理的去使用设计来规避这个问题。

在设计模式中,可以使用工厂模式或者策略模式来处理这类问题,之前已经分享了工厂模式,感兴趣的同学可以去复习一下。

设计模式系列往期文章:

  • 单例模式
  • 工厂模式
  • 流程引擎
  • 建造者模式
  • 原型模式
  • 责任链模式
  • 观察者模式

那么工厂模式和策略模式有什么区别呢?

  • 工厂模式是属于创建型设计模式,主要用来针对不同类型创建不同的对象,达到解偶类对象。
  • 策略模式是属于行为型设计模式,主要是针对不同的策略做出对应行为,达到行为解偶

本次就来具体聊聊策略模式它是如何做到行为解耦

大纲

定义

什么是策略模式?它的原理实现是怎么样的?

定义一系列算法,封装每个算法,并使他们可以互换,不同的策略可以让算法独立于使用它们的客户而变化。 以上定义来自设计模式之美

感觉有点抽象?那就来看一张结构图吧

  • Strategy(抽象策略):抽象策略类,并且定义策略执行入口
  • ConcreteStrategy(具体策略):实现抽象策略,实现algorithm方法
  • Context(环境):运行特定的策略类。

这么看结构其实还是不复杂的,而且跟状态模式类似。

那么这个代码怎么实现?

举个例子,汽车大家肯定都不陌生,愿大家早日完成汽车梦,汽车的不同档(concreteStrategy)就好比不同的策略,驾驶者选择几档则汽车按几档的速度前进,整个选择权在驾驶者(context)手中。

public interface GearStrategy {

    // 定义策略执行方法
    void algorithm(String param);
}
复制代码

首先还是先定义抽象策略

这里是用接口的形式,还有一种方式可以用抽象方法abstract来写也是一样的。具体就看大家自己选择了。

public abstract class GearStrategyAbstract {
 // 定义策略执行方法
 abstract void algorithm(String param);
}
复制代码
public class GearStrategyOne implements GearStrategy {

    @Override
    public void algorithm(String param) {
        System.out.println("当前档位" + param);
    }
}
复制代码

其次定义具体档位策略,实现algorithm方法。

public class Context {
		// 缓存所有的策略,当前是无状态的,可以共享策略类对象
    private static final Map<String, GearStrategy> strategies = new HashMap<>();

    // 第一种写法
    static {
        strategies.put("one", new GearStrategyOne());
    }

    public static GearStrategy getStrategy(String type) {
        if (type == null || type.isEmpty()) {
            throw new IllegalArgumentException("type should not be empty.");
        }
        return strategies.get(type);
    }

    // 第二种写法
    public static GearStrategy getStrategySecond(String type) {
        if (type == null || type.isEmpty()) {
            throw new IllegalArgumentException("type should not be empty.");
        }
        if (type.equals("one")) {
            return new GearStrategyOne();
        }
        return null;
    }


    public static void main(String[] args) {
        // 测试结果
        GearStrategy strategyOne = Context.getStrategy("one");
        strategyOne.algorithm("1档");
         // 结果:当前档位1档
        GearStrategy strategyTwo = Context.getStrategySecond("one");
        strategyTwo.algorithm("1档");
        // 结果:当前档位1档
    }

}
复制代码

最后就是实现运行时环境(Context),你可以定义成StrategyFactory,但都是一个意思。

在main方法里面的测试demo,可以看到通过不同的type类型,可以实现不同的策略,这就是策略模式主要思想。

在Context里面定义了两种写法:

  • 第一种是维护了一个strategies的Map容器。用这种方式就需要判断每种策略是否可以共享使用,它只是作为算法的实现。
  • 第二种是直接通过有状态的类,每次根据类型new一个新的策略类对象。这个就需要根据实际业务场景去做的判断。

框架的应用

策略模式在框架中也在一个很常见的地方体现出来了,而且大家肯定都有使用过。

那就是JDK中的线程池ThreadPoolExecutor

首先都是类似于这样定义一个线程池,里面实现线程池的异常策略。

这个线程池的异常策略就是用的策略模式的思想。

在源码中有RejectedExecutionHandler这个抽象异常策略接口,同时它也有四种拒绝策略。关系图如下:

这就是在框架中的体现了,根据自己的业务场景,合理的选择线程池的异常策略。

业务改造举例

在真实的业务场景中策略模式也还是应用很多的。

在社交电商中分享商品是一个很重要的环节,假设现在要我们实现一个分享图片功能,比如当前有 单商品、多商品、下单、会场、邀请、小程序链接等等多种分享场景。

针对上线这个流程图先用if else语句做一个普通业务代码判断,就像下面的这中方式:

public class SingleItemShare {
    // 单商品
    public void algorithm(String param) {
        System.out.println("当前分享图片是" + param);
    }
}
public class MultiItemShare {
    // 多商品
    public void algorithm(String param) {
        System.out.println("当前分享图片是" + param);
    }
}
public class OrderItemShare {
    // 下单
    public void algorithm(String param) {
        System.out.println("当前分享图片是" + param);
    }
}
public class ShareFactory {

    public static void main(String[] args) throws Exception {
        Integer shareType = 1;
       // 测试业务逻辑
        if (shareType.equals(ShareType.SINGLE.getCode())) {
            SingleItemShare singleItemShare = new SingleItemShare();
            singleItemShare.algorithm("单商品");
        } else if (shareType.equals(ShareType.MULTI.getCode())) {
            MultiItemShare multiItemShare = new MultiItemShare();
            multiItemShare.algorithm("多商品");
        } else if (shareType.equals(ShareType.ORDER.getCode())) {
            OrderItemShare orderItemShare = new OrderItemShare();
            orderItemShare.algorithm("下单");
        } else {
            throw new Exception("未知分享类型");
        }
        // .....省略更多分享场景
    }

    enum ShareType {
        SINGLE(1, "单商品"),
        MULTI(2, "多商品"),
        ORDER(3, "下单");
        /**
         * 场景对应的编码
         */
        private Integer code;
        /**
         * 业务场景描述
         */
        private String desc;
        ShareType(Integer code, String desc) {
            this.code = code;
            this.desc = desc;
        }
        public Integer getCode() {
            return code;
        }
       // 省略 get set 方法
    }
}
复制代码

这里大家可以看到每新加一种分享类型,就需要加一次if else 判断,当如果有十几种场景的时候那代码整体就会非常的长,看起来给人的感觉也不是很舒服。

接下来就看看如何用策略模式进行重构:

public interface ShareStrategy {
    // 定义分享策略执行方法
    void shareAlgorithm(String param);
}

public class OrderItemShare implements ShareStrategy {
    @Override
    public void shareAlgorithm(String param) {
        System.out.println("当前分享图片是" + param);
    }
}

// 省略 MultiItemShare以及SingleItemShare策略

// 分享工厂
public class ShareFactory {
		// 定义策略枚举
    enum ShareType {
        SINGLE("single", "单商品"),
        MULTI("multi", "多商品"),
        ORDER("order", "下单");
        // 场景对应的编码
        private String code;
       
        // 业务场景描述
        private String desc;
        ShareType(String code, String desc) {
            this.code = code;
            this.desc = desc;
        }
        public String getCode() {
            return code;
        }
       // 省略 get set 方法
    }
		// 定义策略map缓存
    private static final Map<String, ShareStrategy> shareStrategies = new       HashMap<>();
    static {
        shareStrategies.put("order", new OrderItemShare());
        shareStrategies.put("single", new SingleItemShare());
        shareStrategies.put("multi", new MultiItemShare());
    }
    // 获取指定策略
    public static ShareStrategy getShareStrategy(String type) {
        if (type == null || type.isEmpty()) {
            throw new IllegalArgumentException("type should not be empty.");
        }
        return shareStrategies.get(type);
    }
	
    public static void main(String[] args) {
        // 测试demo
        String shareType = "order";
        ShareStrategy shareStrategy = ShareFactory.getShareStrategy(shareType);
        shareStrategy.shareAlgorithm("order");
        // 输出结果:当前分享图片是order
    }
}

复制代码

这里策略模式就已经改造完了。在client请求端,根本看不到那么多的if else判断,只需要传入对应的策略方式即可,这里我们维护了一个策略缓存map,在直接调用的ShareFactory获取策略的时候就直接是从换种获取策略类对象。

这就已经达到了行为解偶的思想。同时也避免了长串的if else 判断。

优点:

  • 算法策略可以自由实现切换
  • 扩展性好,加一个策略,只需要增加一个类

缺点:

  • 策略类数量多
  • 需要维护一个策略枚举,让别人知道你当前具有哪些策略

总结

以上就讲完了策略模式,整体看上去其实还是比较简单的,还是那句话学习设计模式我们还是要学习每种设计模式的思想,任何一种设计模式存在即合理。当然也不要因为设计模式而设计代码,那样反而得不偿失。

我是敖丙,你知道的越多,你不知道的越多,感谢各位人才的:点赞收藏评论,我们下期见!


文章持续更新,可以微信搜一搜「 三太子敖丙 」第一时间阅读,回复【资料】有我准备的一线大厂面试资料和简历模板,本文 GitHub github.com/JavaFamily 已经收录,有大厂面试完整考点,欢迎Star。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/75581.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hadoop集群中HDFS的API测试案例以及MapReduce的多种提交Job方式案例

这两个案例默认是hadoop集群环境已经搭建好以及IDEA环境也已经配置好 1、HDFS客户端测试案例 1.1、pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www…

Java使用ftl模板文件生成Word,以及Word转换图片或Pdf工具类

Java使用ftl模板文件生成Word 一、写在前面 最近在项目中使用打印功能&#xff0c;发现这个功能我已经写过多次了&#xff0c;下面这个文章的发步日期在2020年&#xff0c;不得不感慨时间之快啊。 https://blog.csdn.net/weixin_43238452/article/details/109636200?spm1001…

this关键字,是如何把你难倒的?

作为一名实战前端工程师&#xff0c;在jq时代&#xff0c;是经常被this关键字难倒的。几年前每次我意识到程序出现问题的时候&#xff0c;都本能反应是自己的this没有绑定好&#xff0c;于是重新绑定一下&#xff0c;就能解决了。但是他确实一直为难着我。 转眼到了2022年底&a…

图解LeetCode——1780. 判断一个数字是否可以表示成三的幂的和(难度:中等)

一、题目 给你一个整数 n &#xff0c;如果你可以将 n 表示成若干个不同的三的幂之和&#xff0c;请你返回 true &#xff0c;否则请返回 false 。 对于一个整数 y &#xff0c;如果存在整数 x 满足 y 3^x &#xff0c;我们称这个整数 y 是三的幂。 二、示例 2.1> 示例…

SpringBoot面试杀手锏——自动配置原理

引言 不论在工作中&#xff0c;亦或是求职面试&#xff0c;Spring Boot已经成为我们必知必会的技能项。除了某些老旧的政府项目或金融项目持有观望态度外&#xff0c;如今的各行各业都在飞速的拥抱这个已经不是很新的Spring启动框架。 当然&#xff0c;作为Spring Boot的精髓…

凌恩客户文章|JCR 一区:多组学联合分析揭示PCOS真元凶

期刊&#xff1a;Journal of Ovarian Research 影响因子&#xff1a;5.506 发表时间&#xff1a;2022年10月 客户单位&#xff1a;汕头大学医学院第一附属医院鄞国书课题组 一、研究背景 多囊卵巢综合征(PCOS)是导致育龄妇女不孕的最常见内分泌疾病…

MyBatis二 MyBatis常见面试题

一 MyBatis是什么&#xff1f; MyBatis是一款优秀的持久层框架&#xff0c;一个半ORM &#xff08;对象关系映射&#xff09;框架&#xff0c;它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XM…

postman+newman+jenkins持续集成

今天为大家带来的是postmannewmanjenkins进行API接口测试的持续集成: 一. postman测试实战 postman测试API接口是通过JavaScript脚本完成测试中的功能, 在请求发起前后实现测试操作. 常用功能: 请求前脚本(pre-request scripts)设置请求前置操作如设置变量等 请求后在tests…

UE多实例云渲染之Nginx代理

Nginx的Https代理 我们实现UE像素流时大部分使用的是http协议&#xff0c;但是某些时候前端由于某些功能问题需要使用https的协议(如前端调用本地摄像头&#xff09;需要使用https协议。如果此时在内部使用http请求就会被block掉。其中一种简单的方案就是通过修改浏览器的安全…

SOLID设计原则

1. SRP单一职责原则 任何一个软件模块都应该有且仅有一个被修改的原因。 反例是一个类的三个方法给三个部门提供服务&#xff0c;而这三个方法有一些公共子方法&#xff0c;很有可能因为A部门的代码变更改动&#xff0c;导致B、C部门调用的方法出现意料之外的结果。 2. OCP开…

nginx请求转发webSocket

原由 1.我有一个socket服务&#xff0c;用ip访问是成功的&#xff0c;但是老板说用这个不行&#xff0c;要用nginx请求转发&#xff0c;转发内网地址到某个域名 2.但是我改成了指定域名后失败&#xff0c;因为我没有配置nginx.conf 3.接下来一起学习下&#xff0c;怎么在ngin…

华硕电脑怎么录屏?华硕电脑录屏功能在哪?简单实用教程来了

​有很多小伙伴对如何录制华硕电脑的屏幕知识和信息知之甚少。现在大家都想多了解一些关于华硕电脑怎么录屏的相关知识和信息。所以今天小编收集了一些关于如何录制华硕电脑屏幕的知识和信息&#xff0c;与大家分享。 华硕电脑怎么录屏方法一&#xff1a;使用Windows自带的录屏…

day45 PHP反序列化POP链构造魔术方法原生类

前言 #知识点&#xff1a; 1、什么是反序列化操作&#xff1f;-格式转换 2、为什么会出现安全漏洞&#xff1f;-魔术方法 3、反序列化漏洞如何发现&#xff1f; -对象逻辑 4、反序列化漏洞如何利用&#xff1f;-POP链构造 补充&#xff1a;反序列化利用大概分类三类 -魔…

重磅来袭!共600页,又一分牛逼的Python文档面世!

前言 本文是为了帮大家快速学习 Python 中知识点&#xff0c;这套资料涵盖了诸多学习内容&#xff1a;面试题&#xff0c;教材视频&#xff0c;实战资料&#xff0c;电子书籍。 相信可以帮助大家在最短的时间内&#xff0c;能达到事半功倍效果&#xff0c;用来复习也是非常不…

150行代码写个低配版WPS?:手把手教你实现+附完整源码

前言&#xff1a;通过一个简单实例来实现Java的GUI编程&#xff0c;对于新手较为友好&#xff0c;且有一定的实用性&#xff0c;正在学习Java的同学看过来&#xff01;&#xff01;包教包会&#xff0c;如有疑问欢迎私信询问&#xff01; 目录 实例展示 一、GUI编程基本概念 …

java面向对象----抽象类 接口

目录 抽象类与抽象方法 概念 抽象类应用 接 口 概念 接口的特点&#xff1a; 接口应用举例 Java 8中关于接口的改进 内部类 如何声明局部内部类 局部内部类的特点 匿名内部类 总结 抽象类与抽象方法 概念 随着继承层次中一个个新子类的定义&#xff0c;类变得越…

机器学习之多元线性回归

1、多元线性回归模型&#xff1a;定义&#xff1a;具有多个特征的线性回归模型 多元线性回归模型&#xff0c;见下图&#xff1a; 多元线性回归总结&#xff08;参数、模型、损失函数、梯度下降算法&#xff09;见下图&#xff1a;【注意&#xff1a;梯度下降算法每次都是同时…

Vue2中过滤器的用法详解

目录 一、过滤器的概念 二、过滤器的使用位置 三、过滤器的分类 1、全局过滤器 2、本地过滤器 四、过滤器应用实例 1、使用过滤器实现省略号 2、使用过滤器处理时间戳 五、Vue3中已废弃过滤器 过滤器是vue中的一个特性&#xff0c;作用是用于对文本进行格式化的作用。…

动态与静态获取元素

&#x1f335;知识点简介 静态获取&#xff08;querySelectorAll&#xff09;时&#xff0c;选出的所有元素的数组&#xff0c;不会随着文档操作而改变&#xff1b; 动态获取&#xff08;getElementById&#xff09;时&#xff0c;选出的所有元素的数组&#xff0c;会随着文档的…

Paddle 点灯人 之 Tensor

Paddle 点灯人 之 Tensor 文章目录Paddle 点灯人 之 TensorPaddle点灯人介绍Tensor介绍Pytorch和Paddle的相似之处创建张量Tensor图片/文本转TensorDataLoader不需要加ToTensorPaddle中的 Tensor 的属性形状 shape数据类型&#xff08;dtype&#xff09;Tensor 的设备位置&…