设计模式-工厂模式Factory

news2024/12/24 8:32:34

工厂模式

  • b.工厂方法模式 (Factory Method) (重点)
    • 1) 简单工厂 Simple Factory
      • 1.a) 简单工厂的好处
    • 2) 工厂方法 Factory Method
      • 2.a) 对工厂进行抽象化 (版本一)
      • 2.b) 对工厂进行缓存 (版本二)
      • 2.c) 加载配置文件到缓存中 (版本三)
        • c.1) 产品线
  • c.抽象工厂模式 (Abstract Factory)
  • 总结:3种工厂模式

b.工厂方法模式 (Factory Method) (重点)

一般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。

1) 简单工厂 Simple Factory

简单工厂叫作静态工厂方法模式(Static Factory Method Pattern)

现在有一个场景,需要一个资源加载器,要根据不用的url进行资源加载,但是如果我们将所有的加载实现代码全部封装在了一个load方法中,就会导致一个类很大,同时扩展性也非常差,当想要添加新的前缀解析其他类型的url时,发现需要修改大量的源代码。

定义两个需要之后会用到的类

/**
 * 事实上一个资源类会比这个负责
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Resource {
    private String url;
}
public class ResourceLoader {

    /**
     * 资源加载
     * @param url file:// http:// classpath://
     * @return 资源类
     */
    public Resource load(String url) {
        // 1.根据url获取前缀
        String prefix = getPreFix(url);

        Resource resource = null;

        // 2.根据前缀,处理不同的资源(有很多的分支逻辑)
        if ("http".equals(prefix)){
            // 发起http请求下载资源....(代码复杂)
            return new Resource(url);
        } else if ("file".equals(prefix)) {
            // 建立流,做异常...(代码复杂)
            return new Resource(url);
        } else if ("classpath".equals(prefix)) {
            // ......(代码复杂)
            return new Resource(url);
        }else {
            return new Resource("default");
        }
    }

    /**
     * 获取前缀
     * @param url
     * @return
     */
    private String getPreFix(String url) {
        if (url == null || url.equals("") || !url.contains("://")){
            throw new ResourceLoadException("传入的资源url不合法");
        }
        String[] split = url.split(":");
        return split[0];
    }
}

在上边的案例中,存在很多的if分支,如果分支数量不多,且不需要扩展,这样的编写方式当然没错,然而在实际的工作场景中,我们的业务代码可能会很多,分支逻辑也可能十分复杂,这个时候简单工厂设计模式就要发挥作用了。

只需要创建一个工厂类,将创建资源的能力交给工厂即可:

/**
 * 简单工厂
 */
public class ResourceFactory {

    public static Resource create(String type, String url) {
        if ("http".equals(type)){
            // 发起http请求下载资源....(代码复杂)
            return new Resource(url);
        } else if ("file".equals(type)) {
            // 建立流,做异常...(代码复杂)
            return new Resource(url);
        } else if ("classpath".equals(type)) {
            // ......(代码复杂)
            return new Resource(url);
        }else {
            return new Resource("default");
        }
    }
}

有了工厂之后,我们的主要逻辑就会简化:

public Resource load(String url) {
    // 1.根据url获取前缀
    String prefix = getPreFix(url);

    // 2.根据前缀,处理不同的资源(有很多的分支逻辑)
    return ResourceLoaderFactory.create(prefix, url);
}

1.a) 简单工厂的好处

这就是简单工厂设计模式,提取一个工厂类,工厂会根据传入的不同的类型,创建不同的产品,将创建对象的过程交给工厂类、其他业务需要某个产品时,直接使用create()创建,好处如下:

  • 1.工厂将创建的过程进行封装,不需要关系创建的细节,更加符合面向对象思想
  • 2.主要的业务逻辑不会被创建对象的代码干扰,代码更易阅读
  • 3.产品的创建可以独立测试,更将容易测试
  • 4.独立的工厂类只负责创建产品,更加符合单一原则

2) 工厂方法 Factory Method

对上面的代码进行重构。我们会为每一个 Resource 创建一个独立的工厂类,形成一个个小作坊,将每一个实例的创建过程交给工厂类完成。

2.a) 对工厂进行抽象化 (版本一)

回到的例子中,每一种url加载成不同的资源产品,那每一种资源都可以由一个独立的ResourceFactory生产,需要将生产资源的工厂类进行抽象:

public interface IResourceLoader {
    Resource load(String url);
}

为每一种资源创建与之匹配的实现:

// Http
public class HttpResourceLoader implements IResourceLoader {
    @Override
    public Resource load(String url) {
        // 省略复杂处理的创建过程
        return new Resource(url);
    }
}

// File
public class FileResourceLoader implements IResourceLoader {
    @Override
    public Resource load(String url) {
        // 省略复杂处理的创建过程
        return new Resource(url);
    }
}

// Classpath
public class ClasspathResourceLoader implements IResourceLoader {
    @Override
    public Resource load(String url) {
        // 省略复杂处理的创建过程
        return new Resource(url);
    }
}

// Default
public class DefaultResourceLoader implements IResourceLoader {
    @Override
    public Resource load(String url) {
        // 省略复杂处理的创建过程
        return new Resource(url);
    }
}

主要逻辑就会简化

private IResourceLoader resourceLoader;

public Resource load(String url) {
    // 1.根据url获取前缀
    String prefix = getPreFix(url);

    // 2.根据前缀选择不同的工厂,生产独自的产品
    if ("http".equals(prefix)){
        // 发起http请求下载资源....(代码复杂)
        resourceLoader = new HttpResourceLoader();
    } else if ("file".equals(prefix)) {
        // 建立流,做异常...(代码复杂)
        resourceLoader = new FileResourceLoader();
    } else if ("classpath".equals(prefix)) {
        // ......(代码复杂)
        resourceLoader = new ClasspathResourceLoader();
    }else {
        resourceLoader = new DefaultResourceLoader();
    }

    return resourceLoader.load(url);
}

当我们新增一种读取资源的方式时,只需要新增一个实现,并实现 IResourceLoader 接口即可。所以,工厂方法模式比起简单工厂模式更加符合开闭原则。

2.b) 对工厂进行缓存 (版本二)

们为每个产品引入了工厂,却发现需要为创建工厂这个行为付出代价,在创建工厂这件事上,仍然不符合开闭原则

为了解决上述的问题我们又不得不去创建一个工厂的缓存来统一管理工厂实例,以后使用工厂会更加的简单

主要业务逻辑优化为:使用静态代码块和Map进行缓存

// 略....
private static Map<String, IResourceLoader> resourceLoaderCache = new HashMap<>();

static {
    resourceLoaderCache.put("http", new HttpResourceLoader());
    resourceLoaderCache.put("file", new FileResourceLoader());
    resourceLoaderCache.put("classpath", new ClasspathResourceLoader());
    resourceLoaderCache.put("default", new DefaultResourceLoader());
}

/**
 * 资源加载
 * @param url file:// http:// classpath://
 * @return 资源类
 */
public Resource load(String url) {
    // 1.根据url获取前缀
    String prefix = getPreFix(url);

    // 2
    return resourceLoaderCache.get(prefix).load(url);
}
// 略....

2.c) 加载配置文件到缓存中 (版本三)

但是,当修改需求还是不够灵活,仍然需要修改static中的代码,将工厂类进行配置,加载配置文件到缓存中

创建resourceLoad.properties

http=com.dcy.factoryMethod.resourceFactory.impl.HttpResourceLoader
file=com.dcy.factoryMethod.resourceFactory.impl.FileResourceLoader
classpath=com.dcy.factoryMethod.resourceFactory.impl.ClasspathResourceLoader
default=com.dcy.factoryMethod.resourceFactory.impl.DefaultResourceLoader

主要业务逻辑优化为:

// 略...
private static Map<String, IResourceLoader> resourceLoaderCache = new HashMap<>();

static {
    InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("resourceLoad.properties");
    Properties properties = new Properties();
    try {
        properties.load(inputStream);

        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            String key = entry.getKey().toString();
            Class<?> clazz = Class.forName(entry.getValue().toString());
            IResourceLoader loader = (IResourceLoader) clazz.getConstructor().newInstance();
            resourceLoaderCache.put(key, loader);
        }

    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

public Resource load(String url) {
    // 1.根据url获取前缀
    String prefix = getPreFix(url);

    // 2.
    return resourceLoaderCache.get(prefix).load(url);
}

// 略...

好处:

  • 1.完成满足开闭原则。当需要扩充需求是,只需要新增实现和修改配置文件,不需要修改一行代码
  • 2.完全解耦

c.1) 产品线

事实上,在工作中,我们的产品可能是及其复杂的,我们同样需要对整个产品线进行抽象

public abstract class AbstractResource {

    private String url;

    public AbstractResource() {
    }

    public AbstractResource(String url) {
        this.url = url;
    }

    protected void shared() {
        System.out.println("这是共享方法");
    }

    /**
     * 每个子类需要独自实现的方法
     * @return 字节流
     */
    public abstract InputStream getInputStream();

}

为每一种产品创建与之匹配的实现:

// classpath
public class ClasspathResource extends AbstractResource {
    public ClasspathResource() {
    }

    public ClasspathResource(String url) {
        super(url);
    }

    @Override
    public InputStream getInputStream() {
        return null;
    }
}

// Http
public class HttpResource extends AbstractResource {
    public HttpResource() {
    }

    public HttpResource(String url) {
        super(url);
    }

    @Override
    public InputStream getInputStream() {
        return null;
    }
}

// Default
public class DefaultResource extends AbstractResource {
    public DefaultResource() {
    }

    public DefaultResource(String url) {
        super(url);
    }

    @Override
    public InputStream getInputStream() {
        return null;
    }
}

修改IResourceLoader接口,将返回一个产品的资源

public interface IResourceLoader {
    AbstractResource load(String url);
}

修改实现工厂的方法

public class ClasspathResourceLoader implements IResourceLoader {
    @Override
    public AbstractResource load(String url) {
        // 省略复杂处理的创建过程
        return new ClasspathResource(url);
    }
}

主要 加载资源load()业务逻辑修改为

public AbstractResource load(String url) {
    // 1.根据url获取前缀
    String prefix = getPreFix(url);

    // 2.
    return resourceLoaderCache.get(prefix).load(url);
}

在这里插入图片描述

c.抽象工厂模式 (Abstract Factory)

重要性比不上简单工厂和工厂方法

抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式生产需要的对象是一种不错的解决方案。

多种类型的产品,即增加产品族。如:抽象多种产品类型

public interface Resource {
    InputStream getInputStream();
}
// 抽象文本类型
public abstract class AbstractTextResource implements Resource {
    // 略....
}

// 抽象图片类型
public abstract class AbstractPictureResource implements Resource {
	// 略....
}

// 抽象视频资源
public abstract class AbstractVideoResource implements Resource {
    // 略....
}

每一个工厂实例都可以生产一个产品族

/**
 * IResourceFactory,每一个工厂实例都可以生产一个产品族
 */
public interface IResourceLoader {
    /**
     * 加载图片资源的工厂方法
     * @param url 资源路径
     * @return 图片资源
     */
    AbstractPictureResource loadPicture(String url);

    /**
     * 加载视频资源的工厂方法
     * @param url 资源路径
     * @return 视频资源
     */
    AbstractVideoResource loadVideo(String url);

    /**
     * 加载文本资源的工厂方法
     * @param url 资源路径
     * @return 文本资源
     */
    AbstractTextResource loadText(String url);
}

每一个工厂都对应各自的产品生产线

// Classpath
public class ClasspathResourceLoader implements IResourceLoader {

    @Override
    public AbstractPictureResource loadPicture(String url) {
        return new ClasspathPictureResource(url);
    }

    @Override
    public AbstractVideoResource loadVideo(String url) {
        return new ClasspathVideoResource(url);
    }

    @Override
    public AbstractTextResource loadText(String url) {
        return new ClasspathTextResource(url);
    }
}
// Default
public class DefaultResourceLoader implements IResourceLoader {
    @Override
    public AbstractPictureResource loadPicture(String url) {
        return new DefaultPictureResource(url);
    }

    @Override
    public AbstractVideoResource loadVideo(String url) {
        return new DefaultVideoResource(url);
    }

    @Override
    public AbstractTextResource loadText(String url) {
        return new DefaultTextResource(url);
    }
}
// Http
public class HttpResourceLoader implements IResourceLoader {
    @Override
    public AbstractPictureResource loadPicture(String url) {
        return new HttpPictureResource(url);
    }

    @Override
    public AbstractVideoResource loadVideo(String url) {
        return new HttpVideoResource(url);
    }

    @Override
    public AbstractTextResource loadText(String url) {
        return new HttpTextResource(url);
    }
}

在这里插入图片描述

总结:3种工厂模式

简单工厂:一个工厂生产不同产品

静态资源工厂(工厂方法):一个工厂生产一个产品

抽象工厂:一个工厂生产一个产品族,每一个产品族之间可能会有共享或交叉

当创建逻辑比较复杂,是一个“大工程”的时候,我们就应该考虑使用工厂模式,封装对象的创建过程,将对象的创建和使用相分离

何为创建逻辑比较复杂呢?

  • 类似规则配置解析的例子,代码中存在 if-else 分支判断。将这一大坨 if-else 创建对象的代码抽离出来,放到工厂类中。
  • 单个对象本身的**创建过程比较复杂,**比如前面提到的要组合其他类对象,做各种初始化操作。在这种情况下,我们也可以考虑使用工厂模式,将对象的创建过程封装到工厂类中。

工厂模式的作用:

  • 1.封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。
  • 2.代码复用:创建代码抽离到独立的工厂类之后可以复用。
  • 3.隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。
  • 4.控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁,测试更方便

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

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

相关文章

五子棋游戏禁手算法的改进

五子棋游戏禁手算法的改进 五子棋最新的禁手规则&#xff1a; 1&#xff0e;黑棋禁手判负、白棋无禁手。黑棋禁手有“三三”&#xff08;包括“四三三”&#xff09;、“四四”&#xff08;包括“四四三”&#xff09;和“长连”。黑棋只能以“四三”取胜。 2&#xff0e;黑方…

B端系统设计之【权限】详解

衡量B端产品系统好坏的一个重要标准就是它的权限是否细致&#xff0c;拓展性是否强。 权限管理是B端产品系统的基础&#xff0c;好的权限管理系统延展性较强&#xff0c;即使业务发生变化也能避免大改和推倒重建。 权限分类 1、功能权限 功能权限是指用户登录系统之后&#xff…

前端面试中Vue的有经典面试题三

11. 网页从输入网址到渲染完成经历了哪些过程&#xff1f; 大致可以分为如下7步&#xff1a; 输入网址&#xff1b; 发送到DNS服务器&#xff0c;并获取域名对应的web服务器对应的ip地址&#xff1b; 与web服务器建立TCP连接&#xff1b; 浏览器向web服务器发送http请求&a…

【自用】西门子s7-200连接显示屏和物联网盒子完整配置过程

总览 1.PLC配置 2.显示屏配置 3.物联网盒子配置 一、PLC配置 1.连接PLC软件 STEP-7MicroWIN V4.0 SP9完整版 链接&#xff1a;https://pan.baidu.com/s/17LMEXnbkQZMPI8Bte24Eug?pwdjsi3 提取码&#xff1a;jsi3 2.PLC配置 打开 PLC 上面的小盖子&#xff0c;把红色按钮…

【代码随想录day23】不同路径 II

题目 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。那么从左上角到…

3.1.6 练习 基于GPA排名计算本专业保研名单

C自学精简教程 目录(必读) GPA概念回顾 平均学分绩点GPA(Grade Point Average)是对一个学生大学学习成绩的综合的衡量指标。 在前面的文章 本科生学分绩点GPA计算 中&#xff0c;我们知道了什么是平均学分绩点GPA&#xff0c;以及如何计算它。 基于GPA给学生排名 现在我们…

介绍几种使用工具

FileWatch&#xff0c;观测文件变化&#xff0c;源码地址&#xff1a;https://github.com/ThomasMonkman/filewatch nlohmann::json&#xff0c;json封装解析&#xff0c;源码地址&#xff1a;https://github.com/nlohmann/json optionparser&#xff0c;解析选项&#xff0c;源…

4.(Python数模)0-1规划

Python解决0-1规划问题 参考下面文章 源代码 import pulp # 导入 pulp 库# 主程序 def main():# 投资决策问题&#xff1a;# 公司现有 5个拟投资项目&#xff0c;根据投资额、投资收益和限制条件&#xff0c;问如何决策使收益最大。"""问题建模&#x…

9. 微积分 - 导数

文章目录 导数求导实例代码演示:迭代法求解二次函数最小值阶Hi, 大家好。我是茶桁。 我们终于结束了极限和连续的折磨,开启了新的篇章。 不过不要以为我们后面的就会很容易,只是相对来说, 没有那么绕而已。 那么,我们今天开始学习「导数」。 导数 在之前的导论,也就是…

【算法竞赛宝典】求分数精确值

【算法竞赛宝典】求分数精确值 题目描述思路讲解代码展示 题目描述 思路讲解 代码展示 //计算分数的精确值 #include<iostream>using namespace std; int remainder[101], quotient[101]; //remainder:存放除法的余数;quotient:依次存放商的每一位int main() {int m, n,…

[CISCN 2019初赛]Love Math

文章目录 前言考点解题过程 前言 感慨自己实力不够&#xff0c;心浮气躁根本做不来难题。难得这题对我还很有吸引力&#xff0c;也涉及很多知识。只能说我是受益匪浅&#xff0c;总的来说加油吧ctfer。 考点 利用php动态函数的特性利用php中的数学函数实现命令执行利用php7的特…

项目验收有哪些流程?

验收流程 科技计划项目验收/课题验收测试服务的被测对象是国家重大专项、科研课题的软件成果物&#xff0c;可以是一个模块、软件或系统等&#xff0c;也可以是软件套件或软件原型等。测试范围主要来源于课题的合同书/可行吧报告/申报书中的技术指标要求。所出具的科技项目验收…

中东 Shopify 如何使用 Bytebase 构建一站式数据库开发工作流

公司简介 Salla 是一家 2016 年成立&#xff0c;位于沙特麦加的自建站电商平台。 作为中东 Shopify&#xff0c;其最大的特点是支持阿拉伯语建站&#xff0c;并且提供更多适应中东地区特点的本地化服务。截止目前&#xff0c;已有 47,000 家店铺入驻 Salla&#xff0c;商品销售…

并发测试工具 apache-jmeter使用发送post请求JSON数据

目录 1 下载安装 2 汉化 3 创建高并发测试 配置线程组 创建web请求 创建监听器 结果树 汇总报告 为web请求添加token 添加Content-Type用于发送json 4 启动测试 5 查看结果 1 下载安装 官网Apache JMeter - Download Apache JMeter 解压运行 2 2 汉化 打开软件…

CS144环境配置问题

使用wsl来做CS144实验&#xff0c;可能会在编译的时候遇到以下问题&#xff0c;很明显是找不到LIBPCAP&#xff0c;参考链接 使用sudo apt-get install libpcap-dev命令下载该库即可。 CMake Error: The following variables are used in this project, but they are set to N…

什么是精益生产?企业如何实现精益生产?

国内的很多制造企业之所以对“精益生产”持怀疑甚至否度态度&#xff0c;大都经历过实施过程中的“水土不服”难题。抛砖引玉讲一下&#xff1a; 1、精益生产的最典型案例 1991年&#xff0c;在当时整个美国处于一种将被“日本人支配”的恐惧背景下&#xff0c;“精益生产”一…

【FPGA项目】第1个项目,来点个灯吧~

前言 你会FPGA吗&#xff1f;我会点灯&#xff01; 一、概述 作为一名点灯高手&#xff0c;今天就来实际操练一下。 如何点亮&#xff1f; 开发板上带有8个LED灯&#xff0c;我们只需要控制它对应的管脚电平输入即可。 1&#xff1a;灭&#xff1b;0&#xff1a;亮。 如何实…

大语言模型之七- Llama-2单GPU(T4 16G)微调(Fine-tune)

模型预训练colab脚本在github主页面。详见Finetuning_LLama_2_0_on_Colab_with_1_GPU.ipynb 在上一篇博客提到两种改进预训练模型性能的方法Retrieval-Augmented Generation (RAG) 或者 finetuning。本篇博客过一下模型微调。 微调&#xff1a;这是采用预训练的LLM并在较小的…

ESP8266+继电器+MQTT+VUE 实现远程开关灯

超详细教程 – ESP8266继电器MQTTVUE 实现远程开关灯 超详细教程 – ESP8266继电器MQTTVUE 实现远程开关灯 接线图 NC&#xff08;通常闭合&#xff09;与COM&#xff08;公共&#xff09;、NO&#xff08;通常开放&#xff09;与COM 是继电器引脚的不同配置&#xff0c;用于不…

JAVA实现SAP接口

JAVA实现SAP接口 环境spring-bootmaven 1.maven依赖 <dependency><groupId>com.github.virtualcry</groupId><artifactId>sapjco-spring-boot-starter</artifactId><version>3.1.4</version></dependency>2.配置文件 applic…