利用工厂模式和策略模式来替代if else

2023-11-12

对于代码写到一定地步,肯定会遇到很多业务代码的繁琐的if else分支。简单的几个条件可以if else几个,但是一旦后面的条件越来越多,有没有更好的替代让代码看起来没那么臃肿。更好维护,而不是一个if一个if的看过去。

其实是有的,就是工厂模式加策略模式来替代成坨的if else。

一 设计模式理解
首先来分析一下主要用到的两个设计模式

工厂模式:实际上工厂模式写法很多,比如说根据产品还是工厂方法来区分简单工厂和工厂方法模式。

根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式。用于封装和管理对象的创建,是一种创建型模式。

//简单工厂模式
//首先你要有一个工厂,这个工厂实际上有很多流水线,每个流水线做不同的产品:比如手机和平板
public class Factory {
    public Phone make(String type) {
        if(phoneType.equalsIgnoreCase("Phone")){
            return new Phone();
        }
        else if(phoneType.equalsIgnoreCase("Ipad")) {
            return new Ipade();
        }
        return null;
    }
}

//第二步,定义流水线(抽象的,流水线要用到方法)
public interface Equipment{
    void make();
}
//第三步,是流水线来具体实施每个产品流水线的方法(这个是实现具体的)
public class Phone implements Equipment{
    public Phone () {
        this.make();
    }
    @Override
    public void make() {
        // TODO Auto-generated method stub
        System.out.println("make  phone 智能手机 !");
    }
}

public class Ipad implements Equipment{
    public Ipad () {
        this.make();
    }
    @Override
    public void make() {
        // TODO Auto-generated method stub
        System.out.println("make ipad 平板电脑!");
    }
}


我们今天用到的其实有点像是简单工厂模式,就是工厂是一个,但是生产的产品是不同的,对比的是工厂方法模式,工厂接口,多个工厂继承工厂接口,每个工厂其实调用的方法相同但是各自实现完全不同

参照一下这个文章关于工厂模式的
设计模式工厂模式

策略模式
说句实在话,策略模式和工厂模式的抽象工厂方法挺像的。

对比就是策略模式里面有策略角色和具体策略角色,对比抽象工厂是抽象工厂和具体工厂。但是有一个context上下文来维护具体策略角色的实现

这个有个博客不错可以看一下
策略模式介绍

//抽象的策略
public abstract class Strategy {

    //算法方法
    public abstract void algorithmInterface();

}

//具体实现的策略,毕竟光抽象不行,还要给各个情况具体的策略
public class ConcreteStrategyA extends Strategy {

    @Override
    public void algorithmInterface() {
        System.out.println("策略A实现");
    }

}

public class ConcreteStrategyB extends Strategy {

    @Override
    public void algorithmInterface() {
        System.out.println("策略B实现");
    }

}
//上下文context,用来维护策略,实际要访问具体的策略要从context中获取
public class Context {

    Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    //上下文接口
    public void contextInterface() {
        strategy.algorithmInterface();
    }

}

//使用的时候创建有具体策略的上下文
public class Client {

    public static void main(String[] args) {
        Context context;

        context = new Context(new ConcreteStrategyA());
        context.contextInterface();

        context = new Context(new ConcreteStrategyB());
        context.contextInterface();

        context = new Context(new ConcreteStrategyC());
        context.contextInterface();
    }

}

二 使用工厂模式+策略模式来替代if else

前面讲到了工厂模式和策略模式是什么,大概的写法(工厂模式写的肯定不全,可以看一下我链接的文章来看一下更多的工厂方式,其实相当于同一个操作提供给更多实现方式,来适应不同的情况)

实际上,就是利用工厂模式生成管理不同的策略(在这一步,工厂类特别像策略模式里面的上下文环境context),定好策略(这个跟抽象工厂方法很像,但是又有很大不同,抽象工厂加一个方法,所有的实现的方法具体工厂都要实现,但是利用了策励模式就不同,工厂只管管理调用方法,真正要用到的都是策励来提供),然后实现不同的具体策略

可以这么说,策励模式解决了一个工厂模式的问题,“我有的,你们实现的都要有”,现在工厂只是提供一个使用策略的方法,不关心你制定多少个策略,每个策励有多少实现。解耦的同时,还做到开-闭原则:对扩展开放,对修改关闭

具体例子:
光说不练假把式,我这里是根据不同的操作查询资产来限制资产是否可以展出,有问题会给个错误码,同样也会把资产数据返回。

(1)创建工厂
工厂维护策略模式,以及具体实现的策略模式的注册,注册后的策略存在一个维护策略的map里面,适用工厂直接通过type类型作为code值来获取策略map里面的策略。

public class ValidAssetByTypeFactory {

    private static Map<String,ValidateAssetService> validAssetTypeMap = new HashMap<>();

    /**
     * 用来维护根据类型验证资产的规则的map
     * @param type
     * @return
     */
    public static ValidateAssetService getInvokeStrategyMap(String type){
        return validAssetTypeMap.get(type);
    }

    /**
     * 注册(创建实现类直接注册即可,不需要在该类放进策略map里面)
     * @param type
     * @param validateAssetService
     */
    public static void register(String type, ValidateAssetService validateAssetService){
        if(StringUtil.isEmpty(type)||null == validateAssetService){
            return;
        }
        validAssetTypeMap.put(type,validateAssetService);
    }

}

(2)创建策略
这个就直接解耦工厂模式的,“我有你们都要有“。策励是一个接口,具体策略就是实现这个接口,在策略模式里面,可以直接获取实现的具体策略。

注意点就是这个 extends InitializingBean不能少,全靠这个往工厂类里面的静态map里面塞值呢。

以下是一个例子,实际上肯定会有很多实现的具体策略。

/**
 * 关于扫码验证资产的策略接口
 * @Author: zhangpeng
 * @Date: 2021/9/9 13:49
 */
public interface ValidateAssetService extends InitializingBean {

    Result<List<DetailsAppAsset>> validateAssetByType(List<AssetDO> assetDOList,String type);
}

(3)具体的实现策略
这个就根据实际情况来实现,造火箭用造火箭的制造方法,造螺丝用造螺丝的方法。

@Component
public class RepairAssetValidate implements ValidateAssetService {
    @Override
    public Result<List<DetailsAppAsset>> validateAssetByType(List<AssetDO> assetDOList, String type) {
        if( CollUtil.isEmpty(assetDOList)  ){
            return resultCreator.create(AssetErrorCode.ASSET_NOT_EXISTS, EntityConverter.convertList(assetDOList, DetailsAppAsset.class));
        }
        for(AssetDO assetDO : assetDOList){
            if( assetDO.getQuoteOrderId() != null ){
                return resultCreator.create(AssetValidErrorCode.ASSET_IN_OTHER_OPERATOR,"资产已被"+ QuoteOrderType.getQuoteOrderType(assetDO.getQuoteOrderType())+"引用" ,EntityConverter.convertList(assetDOList, DetailsAppAsset.class));
            }
        }
        return resultCreator.create(ErrorCode.SUCCESS, EntityConverter.convertList(assetDOList, DetailsAppAsset.class));
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ValidAssetByTypeFactory.register(AssetValidateType.REPAIR.getValue(),this);
    }
    @Autowired
    private ResultCreator resultCreator;
}

(4)做一个维护策略的枚举
其实这个可以不做,直接用字符串来获取策略map里面的具体策略也可以。

但是考虑到要管理起来,别最后起名没有规范,重名,或者不知道到底有多少相关的策略,实际上通过枚举管理起来挺好的,你想绕过这一步也是了可以的,毕竟不是强相关的一步。

public enum AssetValidateType  implements StringOption {

    ALLOCATE("allocate"),
    BATCH_UPDATE_ASSET("batchUpdateAsset"),
    UPDATE_BY_RESIDUAL("updateByResidual"),
    CHANGE_USER_ASSET("changeUserAsset"),
    BACK_ORDER("backOrder"),
    BORROW_RETURN("borrowReturn"),
    BORROW("borrow"),
    COLLECT("collect"),
    DISPOSE("dispose"),
    RECYCLE("recycle"),
    REPAIR("repair")
    ;

    private String value;

    AssetValidateType(String v){
        setValue(v);
    }

    @Override
    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public String getValue() {
        return this.value;
    }

    @Override
    public boolean equals(String i) {
        return this.getValue().equals(i);
    }
}

好了这样就是一个完整的工厂模式+策略模式替代if else的方式
好处是代码看起来更简洁,不再是一大堆if else,而且方便扩展,不必扩展一个业务条件就写一个if的条件限制。

坏处是如果不是很复杂的业务这么写,会有设计过度的情况,对代码读写理解有一定要求,肯定没有入门那种if else方便看懂(其实shiro这些框架具有类似的设计,我当时看授权的这部分就很懵,不能直接debug一个一个往下跳,你要找最后实现的部分才能最后找对地方debug)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

利用工厂模式和策略模式来替代if else 的相关文章

随机推荐

  • 免费商品信息查询接口(条形码)

    最近公司有一个需求 扫描商品条形码显示商品信息 原以为国内应该会免费提供接口 理想总是美好的 现实都是残酷的 在阿里云 京东等API开放平台找了一番 基本都是按次调用收费 公司的需求每位用户一天可能多次调用接口 这样一算 成本太高 既然没有
  • thrift介绍及应用(二)—简单应用

    原文 http blog csdn net guxch article details 12162131 六 一个最简单的实例 Thrift文件 demo thirft 来自官网 如下 plain view plain copy struc
  • 力扣160链表相交(c++版)

    力扣160链表相交 c 力扣题目链接 思路 如果两个链表相交 又都不存在环 那么不难想象这两个链表共同构成了一个Y型 相交部分全部都相同 两链表交点处指针相等 声明指针A指向链表A的头结点 指针B指向链表B的头结点 我们求出两个链表的长度
  • 【基础】创建react脚手架

    React day01 Hello world 一 升级node 1 进入官网 https nodejs org en 2 重新下载最新版本 3 重新安装 一直选择next 既会被覆盖 4 输入 node v 查看最新版本 注 window
  • vue项目中用 cdn 优化

    在我们写项目中 优化问题是不容忽视的 尤其是首屏优化更是重中之重 这里介绍两种方法优化方法 cdn和异步加载 异步请看 http blog csdn net gang456789 article details 78224751 1 cdn
  • Fiddler 八个实用技巧

    目录 前言 1 双击Session时 使响应页始终显示到 json tab页 使请求页始终显示到 webform tab页 2 显示每个Session 的请求IP地址 3 修改响应Header中的Content Type 4 右键sessi
  • 2023AE软件、Adobe After Effects安装下载教程

    2023AE软件是一款由Adobe公司开发的视频编辑软件 也被称为Adobe After Effects 它在广告 电影 电视和网络视频等领域广泛应用 用于制作动态图形 特效 合成和其他视觉效果 该软件支持多种视频和音频文件格式 具有丰富的
  • mybatis增删改查

    MyBatis 是一款优秀的持久层框架 它支持定制化 SQL 存储过程以及高级映射 MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集 MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息 将接口和
  • C语言第五章第3节用do...while语句实现循环学习导案

    课 题 5 3 用do while语句实现循环 课时安排 2课时 课 型 新授 学 习目标 掌握do while循环语句的一般形式 掌握do while循环语句的执行过程 掌握do while语句和while语句的区别 重点 do whil
  • STM32F4 IAP实现总结

    目录 IAP相关 IAP概念 IAP与ICP ISP的区别 STM32F4的启动模式 FLASH相关 STM32F4 FLASH简介 STM32的内部闪存组织架构和其启动过程 应用IAP时的FLASH分配 IAP工程在Keil中的设置 跳转
  • 通过深度学习实现安全帽佩戴的检测

    北京 上海巡回站 NVIDIA DLI深度学习培训 2018年1月26 1月12日 NVIDIA 深度学习学院 带你快速进入火热的DL领域 阅读全文 gt
  • 广度优先遍历(邻接表,邻接矩阵)

    广度优先遍历又称为广度优先搜索 简称BFS 如果说图的深度优先遍历 图的深度优先遍历相关内容 图的深度优先遍历 类似树的前序遍历 那么图的广度优先遍历就类似于树的层序遍历 具体遍历过程如下图所示 就如上面的第三个图上所编写的序号进行遍历 我
  • vue axios请求设置content-type无效

    vue admin template开发中在request请求中设置了Content type 但是在在浏览器中查看请求主体并没有Content type service interceptors request use config gt
  • Python刷题-6

    1 下面哪个不是Python合法的标识符 B A int32 B 40XL C self D name python中的标识符 1 第一个字符必须是字母表中字母或下划线 2 标识符的其他的部分由字母 数字和下划线组成 3 标识符对大小写敏感
  • 搭建ROS & CLion开发环境

    翻译自http wiki ros org IDEs CLion 有小改动 更新 搭建ROS2 CLion开发环境 1 插件 目前CLion有三个积极维护的ROS插件 Hatchery ROS Robot Operating System R
  • M1卡性能简介及存取控制字节规则详解

    M1卡性能简介及存取控制字节规则详解 一 主要指标 容量为 8K 位 EEPROM 分为 16 个扇区 每个扇区为 4 块 每块 16 个字节 以块为存取单位 每个扇区有独立的一组密码及访问控制 每张卡有唯一序列号 为 32 位 具有防冲突
  • 【华为OD机试 2023】 相同数字组成图形的周长(C++ Java JavaScript Python)

    在线OJ 已购买本专栏用户 请私信博主开通账号 在线刷题 运行出现 Runtime Error 0Aborted 请忽略 华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一
  • jdbc-plus是一款基于JdbcTemplate增强工具包,基于JdbcTemplate已实现分页、多租户、动态表名等插件,可与mybatis、mybatis-plus等混合使用

    jdbc plus简介 jdbc plus是一款基于JdbcTemplate增强工具包 基于JdbcTemplate已实现分页 多租户 动态表名等插件 可与mybatis mybatis plus等混合使用 还可以十分简单的扩展自定义插件
  • Android studio 接入腾讯TBSx5内核的解决方案

    Android studio 接入腾讯TBSx5内核的解决方案 首页我们查看一下腾讯浏览服务的官网 https x5 tencent com tbs guide sdkInit html 二 背景知识 1 TBS 腾讯浏览服务 的优势 1
  • 利用工厂模式和策略模式来替代if else

    对于代码写到一定地步 肯定会遇到很多业务代码的繁琐的if else分支 简单的几个条件可以if else几个 但是一旦后面的条件越来越多 有没有更好的替代让代码看起来没那么臃肿 更好维护 而不是一个if一个if的看过去 其实是有的 就是工厂