工厂模式 详解 设计模式

news2024/11/21 0:28:06

工厂模式

其主要目的是封装对象的创建过程,使客户端代码和具体的对象实现解耦。这样子就不用每次都new对象,更换对象的话,所有new对象的地方也要修改,违背了开闭原则(对扩展开放,对修改关闭)。使用工厂来生产对象,更换对象也直接在工厂更换即可。

工厂模式的主要好处包括:

  1. 解耦合:工厂模式将对象的创建过程与客户端代码分离,客户端不需要知道具体的对象是如何创建的,只需要通过工厂方法获取对象即可,从而降低了代码之间的耦合度。
  2. 灵活性:由于工厂负责创建对象,客户端可以通过工厂方法获取不同的对象实例,而无需关心具体的实现细节,从而提高了系统的灵活性。
  3. 可扩展性:如果需要添加新的产品类型,只需在工厂中添加相应的产品创建逻辑,而不需要修改客户端代码,这样可以很方便地扩展系统的功能。
  4. 统一管理:工厂模式将对象的创建集中在一个地方,便于统一管理和维护,提高了代码的可维护性。

使用场景:

  • 当一个系统需要创建多个类型的对象,并且这些对象之间存在着共同的接口时,可以考虑使用工厂模式。
  • 当客户端不需要知道具体的对象是如何创建的,只需要获取对象实例时,可以使用工厂模式。
  • 当系统需要动态地决定创建哪种类型的对象时,可以使用工厂模式。

工厂模式包含以下几个核心角色:

  • 抽象产品(Abstract Product):**定义了产品的共同接口或抽象类。**它可以是具体产品类的父类或接口,规定了产品对象的共同方法
  • 具体产品(Concrete Product):实现了抽象产品接口,定义了具体产品的特定行为和属性。
  • 抽象工厂(Abstract Factory):声明了创建产品的抽象方法,可以是接口或抽象类。它可以有多个方法用于创建不同类型的产品。
  • 具体工厂(Concrete Factory):实现了抽象工厂接口,负责实际创建具体产品的对象

这里介绍三种工厂

  • 简单工厂模式(不属于GOF的23种经典设计模式)
  • 工厂方法模式
  • 抽象工厂模式

简单工厂模式

简单工厂不是一种设计模式,反而比较像是一种编程习惯。

结构:

简单工厂包含如下角色:

  • 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品 :实现或者继承抽象产品的子类
  • 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。

使用场景

  • 当对象的创建逻辑相对简单,并且不需要频繁地进行变更时,可以考虑使用简单工厂模式。
  • 在客户端只知道所需产品的名称或类型,而不需要关心产品的创建过程时,可以使用简单工厂模式。

实现思路:

在这里插入图片描述

// 抽象产品接口
interface Product {      
    void show();
}

// 具体产品类A
class ConcreteProductA implements Product {
    @Override
    public void show() {
        System.out.println("This is product A.");
    }
}

// 具体产品类B
class ConcreteProductB implements Product {
    @Override
    public void show() {
        System.out.println("This is product B.");
    }
}

// 简单工厂类
class SimpleFactory {
    public static Product createProduct(String type) {
        if ("A".equals(type)) {
            return new ConcreteProductA();
        } else if ("B".equals(type)) {
            return new ConcreteProductB();
        }
        return null;
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Product productA = SimpleFactory.createProduct("A");
        productA.show();
        
        Product productB = SimpleFactory.createProduct("B");
        productB.show();
    }
}

上面的工厂类创建对象的功能定义为静态的,这个属于是静态工厂模式,当然你也可以不设置为静态的。

优缺点:

优点:

  • 简单工厂模式中,客户端通过工厂类的静态方法来获取产品实例,而不需要直接实例化具体产品类。如果要实现新产品直接修改工厂类,而不需要在原代码中修改。

缺点:

  • 工厂类负责创建所有产品,因此如果系统需要添加新的产品类型,需要修改工厂类,违反了开放封闭原则。

工厂方法模式

使用工厂方法模式可以完美的解决简单工厂模式的缺点,完全遵循开闭原则。

概念

定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

结构

工厂方法模式的主要角色:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

图例

使用工厂方法模式对上例进行改进:
在这里插入图片描述

工厂方法模式适用于需要创建一系列相关对象的情况

// 抽象产品接口
interface Product {
    void show();
}

// 具体产品类A
class ConcreteProductA implements Product {
    @Override
    public void show() {
        System.out.println("This is product A.");
    }
}

// 具体产品类B
class ConcreteProductB implements Product {
    @Override
    public void show() {
        System.out.println("This is product B.");
    }
}

// 抽象工厂类
interface Factory {
    Product createProduct();
}

// 具体工厂类A,负责创建产品A
class ConcreteFactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}

// 具体工厂类B,负责创建产品B
class ConcreteFactoryB implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Factory factoryA = new ConcreteFactoryA();
        Product productA = factoryA.createProduct();
        productA.show();
        
        Factory factoryB = new ConcreteFactoryB();
        Product productB = factoryB.createProduct();
        productB.show();
    }
}

于是乎要增加产品类时只要相应地增加工厂类,不需要修改工厂类的代码了,这样就解决了简单工厂模式的缺点。

使用场景

  • 当需要创建的对象是一个具体的产品,但是不确定具体产品的类型时,可以使用工厂方法模式。
  • 在工厂类中定义一个创建产品的抽象方法,由子类负责实现具体产品的创建过程,从而实现了产品的创建和客户端的解耦

优缺点

优点:

  • 工厂方法模式中,客户端通过调用工厂类的方法来创建产品,具体产品的创建逻辑由子类实现,不同的产品由不同的工厂子类负责创建。
  • 工厂方法模式符合开放封闭原则,因为客户端可以通过新增工厂子类来添加新的产品类型,而无需修改原有的代码。

缺点:

  • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

抽象工厂模式

抽象工厂模式通常涉及一族相关的产品,每个具体工厂类负责创建该族中的具体产品。

使用场景

  • 当一个系统需要创建一系列相互关联或相互依赖的产品对象时,可以考虑使用抽象工厂模式。
  • 抽象工厂模式提供了一个创建一组相关或相互依赖对象的接口,客户端可以通过该接口来创建产品族中的不同产品,而不需要关心具体的产品实现。

所以由此也可看出,普通工厂模式,工厂方法模式都只是单一产品类的工厂;而很多时候我们需要综合性的,需要生产多等级产品的工厂。下图所示横轴是产品等级,也就是同一类产品;纵轴是产品族,也就是同一品牌的产品,同一品牌的产品产自同一个工厂:

在这里插入图片描述

结构

抽象工厂模式的主要角色如下:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

请添加图片描述

代码案例:

// 抽象产品接口
interface Product {
    void show();
}

// 具体产品类A
class ConcreteProductA implements Product {
    @Override
    public void show() {
        System.out.println("This is product A.");
    }
}

// 具体产品类B
class ConcreteProductB implements Product {
    @Override
    public void show() {
        System.out.println("This is product B.");
    }
}

// 抽象工厂接口
interface AbstractFactory {
    Product createProductA();
    Product createProductB();
}

// 具体工厂类,负责创建产品A和产品B
class ConcreteFactory implements AbstractFactory {
    @Override
    public Product createProductA() {
        return new ConcreteProductA();
    }

    @Override
    public Product createProductB() {
        return new ConcreteProductB();
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        AbstractFactory factory = new ConcreteFactory();
        Product productA = factory.createProductA();
        productA.show();
        
        Product productB = factory.createProductB();
        productB.show();
    }
}
  • 使用场景

    • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
    • 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
    • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

优缺点

优点:

当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点:

当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。

模式扩展 (利用反射机制来创建对象)

简单工厂+配置文件解除耦合

可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。

通过使用配置文件,将创建对象的参数存储在外部配置文件中,可以在不修改客户端代码的情况下,通过修改配置文件来改变对象的创建方式。这样就可以实现对创建逻辑的解耦合,客户端不需要知道具体的创建方式,只需要从工厂类获取对象即可。

具体实现步骤如下:

  1. 在配置文件中配置需要创建的对象的类名或者类型。
  2. 在简单工厂类中读取配置文件,并根据配置的信息来创建对应的对象。

假设有一个配置文件 config.properties,内容如下:

product.type=ConcreteProductA

创建简单工厂类 SimpleFactory.java,用于读取配置文件并根据配置创建对象:

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class SimpleFactory { 
    public static Product createProduct() {
        Properties properties = new Properties();
        try (InputStream inputStream = SimpleFactory.class.getResourceAsStream("config.properties")) {
            properties.load(inputStream);
            String productType = properties.getProperty("product.type");
            if ("ConcreteProductA".equals(productType)) {
                return new ConcreteProductA();
            } else if ("ConcreteProductB".equals(productType)) {
                return new ConcreteProductB();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

在日常开发中,设计模式中的一些常用模式包括:

  1. 单例模式 (Singleton):用于确保一个类只有一个实例,并提供全局访问点,例如数据库连接池、日志系统等。
  2. 工厂模式 (Factory):用于创建对象的接口,但是由子类决定要实例化的类是哪一个。例如,可以用工厂模式创建各种类型的文件解析器。
  3. 观察者模式 (Observer):用于建立对象之间一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知。例如,GUI界面中的事件监听器。
  4. 策略模式 (Strategy):定义一系列算法,将每个算法封装起来,并使它们可以互换。例如,根据用户的选择使用不同的支付策略。
  5. 装饰器模式 (Decorator):动态地给一个对象添加一些额外的职责。例如,在图像处理中可以使用装饰器来添加滤镜效果。
  6. 模板方法模式 (Template Method):定义一个算法的骨架,允许子类为一个或多个步骤提供实现。例如,在构建流程中的各个阶段都有固定的步骤,但是具体实现可能不同。

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

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

相关文章

spring6学习笔记

1.环境准备 1.idea建立一个空项目,jdk要求是17 2.Maven配置(和mybatis里一样) 3.新建一个模块 2.ocp原则 3.依赖倒置原则(DIP) 什么是依赖倒置原则? 1.面向接口编程,面向抽象编程,不要面向…

Windows安装VNC连接工具并结合cpolar实现远程内网Ubuntu系统桌面

文章目录 前言1. ubuntu安装VNC2. 设置vnc开机启动3. windows 安装VNC viewer连接工具4. 内网穿透4.1 安装cpolar【支持使用一键脚本命令安装】4.2 创建隧道映射4.3 测试公网远程访问 5. 配置固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址5.3 测试…

回溯【基础算法精讲 14】

视频地址 : 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 基本概念 1 . 例子 例如从abc和def(n 2)中各选出一个组成新的字符串? 如果n很大 , 这个时候for循环的表达能力有限 ; 2 . 原问题 和 子问题 3 . 增量构造答案 这个增量构造答案的过程就是回溯…

java之Bean对象

1. 什么是Bean? Bean被实例化的,是被Spring框架所管理的Java对象。 Spring容器会自动完成Bean的实例化。将所创建的的Bean自动注入到Ioc容器中以供调用。 spring框架中 IOC容器中管理的对象就是Bean对象 2. 第三方bean Bean 因为第三方bean&#xff0…

SQL函数学习记录

聚合函数 函数是编程语言的基础之一,在对数字的运算中,我们用的最多的就是聚合函数,本篇接下来就详细阐述下SQL中聚合函数的运用。 什么是聚合函数(aggregate function)? 聚合函数指的是对一组值执行计算…

手撕LRU缓存——LinkedHashMap简易源码

题目链接:https://leetcode.cn/problems/lru-cache/description/?envTypestudy-plan-v2&envIdtop-100-liked 原理非常简单,一个双端链表配上一个hash表。 首先我们要知道什么是LRU就是最小使用淘汰。怎么淘汰,链表尾部就是最不常用的直接…

92、评估代码生成操作带来的性能提升

本节评估一下,通过代码生成操作之后,对于模型的性能提升。 评估下性能 在相同的环境下,分别运行 4th_no_malloc 和 5th_codegen 下的 compile.sh 脚本进行代码编译,然后运行编译后生成的可执行文件 ./resnet。 可以分别获取到权值预加载前后的性能指标。 注意:不同电脑机…

可视化图文报表

Apache Echarts介绍 Apache Echarts是一款基于Javascript的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表。 官网&#xff1a;Apache ECharts 入门案例&#xff1a; <!DOCTYPE html> <html>…

Git教程-Git的基本使用

Git是一个强大的分布式版本控制系统&#xff0c;它不仅用于跟踪代码的变化&#xff0c;还能够协调多个开发者之间的工作。在软件开发过程中&#xff0c;Git被广泛应用于协作开发、版本管理和代码追踪等方面。以下是一个详细的Git教程&#xff0c;我们将深入探讨Git的基本概念和…

基于大模型思维链(Chain-of-Thought)技术的定制化思维链提示和定向刺激提示的心理咨询场景定向ai智能应用

本篇为个人笔记 记录基于大模型思维链&#xff08;Chain-of-Thought&#xff09;技术的定制化思维链提示和定向刺激提示的心理咨询场景定向ai智能应用 人工智能为个人兴趣领域 业余研究 如有错漏欢迎指出&#xff01;&#xff01;&#xff01; 目录 本篇为个人笔记 记录基…

【算法】最小生成树—Prim算法与Kruskal算法

Prim算法和Kruskal算法都是解决最小生成树问题的经典算法。最小生成树是原图的最小连通子图&#xff0c;它包含原图的全部结点&#xff0c;且保持图连通的所有边代价和最小。一个连通图可能有多个最小生成树。 一、Prim算法 含义 Prim算法&#xff0c;也被称为普里姆算法&…

项目解决方案: 实时视频拼接方案介绍

目 录 1、实时视频拼接概述 2、适用场景 3、系统介绍 3.1拼接形式 3.1.1横向拼接 3.1.2纵向拼接 3.2前端选择 3.2.1前端类型 3.2.2推荐配置 3.3后端选择 3.3.1录像回放 3.3.2客户端展示 4、拼接方案介绍 4.1基于4K摄像机的拼接方案 4.1.1系统架构…

ESP8266智能家居(3)——单片机数据发送到mqtt服务器

1.主要思想 前期已学习如何用ESP8266连接WIFI&#xff0c;并发送数据到服务器。现在只需要在单片机与nodeMCU之间建立起串口通信&#xff0c;这样单片机就可以将传感器测到的数据&#xff1a;光照&#xff0c;温度&#xff0c;湿度等等传递给8266了&#xff0c;然后8266再对数据…

typescript 的常用方式

文章目录 前言一、绑定props 默认值的方式&#xff1a;withDefaults1.vue2 的props设置默认值2.vue3 的props设置默认值(1) 不设置默认值的写法(2) 设置默认值的写法&#xff08;分离模式&#xff09;(3) 设置默认值的写法&#xff08;组合模式&#xff09; 二、定义一个二维数…

Qt|QTreewidget类下函数qt助手详解说明示例(上)

该系列持续更新&#xff0c;喜欢请一键三连&#xff0c;感谢各位大佬。 QT5.14.2 参考官方QT助手 文章目录 QTreeWidget ClasspropertiesPublic Functions默认构造函数默认析构函数添加根节点void addTopLevelItem(QTreeWidgetItem *item)添加多个根节点void addTopLevelItems…

Linux Shell脚本练习(一)

一、 Linux下执行Shell脚本的方式&#xff1a; 1、用shell程序执行脚本&#xff1a; a、根据你的shell脚本的类型&#xff0c;选择shell程序&#xff0c;常用的有sh&#xff0c;bash&#xff0c;tcsh等 b、程序的第一行#!/bin/bash里面指明了shell类型的&#xff0c;比如#!/…

linux查看服务器内核CUP版本相关命令

服务器参考 计算架构&#xff1a;x86-64产品系列&#xff1a;华为云耀云服务器操作系列&#xff1a;CentOS 7 执行uname -a查看服务器内核版本 Linux hecs-82210 3.10.0-1160.92.1.el7.x86_64 #1 SMP Tue Jun 20 11:48:01 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux执行hostn…

Aws Ec2服务器设置密码登录

通过密钥&#xff0c;ssh登录到服务器 切换到root sudo -i开始设置root的新密码 passwd root输入并确认新密码即可 5.修改ssh配置文件 vim /etc/ssh/sshd_config6.重启sshd配置 systemctl restart sshd

关于电脑一天24小时多少度电电脑的一天用电量计算

随着这几年物价的上涨&#xff0c;一些地区的电价越来越高&#xff0c;而我们经常需要使用电脑&#xff0c;那么一台电脑一天24小时用多少度电呢&#xff1f; 如何计算电脑一天的用电量&#xff1f; 让我们跟随小编来了解更多吧。 1、功耗、主机箱功耗 现在的计算机中&#xf…

2000-2022年上市公司绿色专利申请占比/数据

2000-2022年上市公司绿色专利申请占比数据 1、时间&#xff1a;2000-2022年 2、来源&#xff1a;国家知识产权局、WIPO绿色专利清单 3、指标&#xff1a;年份、股票代码、股票简称、行业代码、省份、城市、区县、行政区划代码、城市代码、区县代码、首次上市年份、上市状态、…