设计模式之 工厂模式

工厂模式(Factory Pattern)是一种创建对象的设计模式,属于 创建型模式(Creational Pattern)之一。其主要目的是通过定义一个工厂方法来创建对象,而不是直接通过 new 关键字实例化对象,从而将对象的创建过程与使用过程分离。这种方式能够减少代码中的耦合度,提高代码的可维护性、可扩展性,并符合面向对象设计的 单一职责原则开放封闭原则

工厂模式的核心思想是让子类决定实例化哪一个类,而父类提供创建对象的接口。它将对象的创建推迟到子类中执行。


1. 工厂模式的结构

工厂模式主要由以下几部分组成:

  1. 抽象产品(Product): 定义了产品的抽象接口,所有具体产品都会实现这个接口。

  2. 具体产品(ConcreteProduct): 实现了抽象产品接口的具体类,提供具体的实现。

  3. 抽象工厂(Creator): 定义了一个工厂方法,返回一个产品对象。此方法可以是抽象方法,也可以是具体方法,具体由子类实现。

  4. 具体工厂(ConcreteCreator): 实现了抽象工厂中的工厂方法,并返回具体的产品对象。


2. 工厂模式的分类

工厂模式可以根据工厂方法的数量和使用方式,分为以下几种类型:

  1. 简单工厂模式(Simple Factory): 又称静态工厂方法模式,它通过一个工厂类来创建对象。这个工厂类决定了具体产品的类型。简单工厂模式并不严格属于设计模式,因为它不符合开闭原则(即扩展时需要修改工厂类)。但它的实现比较简单,适用于产品类型固定的场景。

    优点

    • 客户端只依赖工厂类提供的接口,而不需要了解对象的具体类。
    • 代码可读性高,易于理解。

    缺点

    • 违反了开闭原则,如果产品需要扩展,必须修改工厂类。
    • 增加了工厂类的复杂度,尤其在产品种类较多时,工厂类会变得庞大。

    示例代码:

    // 抽象产品
    public interface Product {
        void doSomething();
    }
    
    // 具体产品A
    public class ProductA implements Product {
        @Override
        public void doSomething() {
            System.out.println("Product A is doing something.");
        }
    }
    
    // 具体产品B
    public class ProductB implements Product {
        @Override
        public void doSomething() {
            System.out.println("Product B is doing something.");
        }
    }
    
    // 工厂类
    public class SimpleFactory {
        public static Product createProduct(String type) {
            if ("A".equals(type)) {
                return new ProductA();
            } else if ("B".equals(type)) {
                return new ProductB();
            } else {
                return null;
            }
        }
    }
    
    // 客户端代码
    public class Client {
        public static void main(String[] args) {
            Product product = SimpleFactory.createProduct("A");
            product.doSomething();
        }
    }
    

  2. 工厂方法模式(Factory Method): 也叫做多态工厂方法,它通过定义一个抽象工厂类,允许子类去决定实例化哪一个具体产品。每个具体工厂类负责创建一个具体的产品。工厂方法模式使得扩展产品种类变得更加容易,符合 开闭原则

    优点

    • 每个工厂只负责创建一个具体的产品,符合单一职责原则。
    • 增加新产品时,只需增加新的具体产品类和具体工厂类,而无需修改现有代码。

    缺点

    • 需要为每个产品创建一个工厂类,增加了类的数量和复杂度。

    示例代码:

    // 抽象产品
    public interface Product {
        void doSomething();
    }
    
    // 具体产品A
    public class ProductA implements Product {
        @Override
        public void doSomething() {
            System.out.println("Product A is doing something.");
        }
    }
    
    // 具体产品B
    public class ProductB implements Product {
        @Override
        public void doSomething() {
            System.out.println("Product B is doing something.");
        }
    }
    
    // 抽象工厂
    public abstract class Creator {
        public abstract Product factoryMethod();
    }
    
    // 具体工厂A
    public class ConcreteCreatorA extends Creator {
        @Override
        public Product factoryMethod() {
            return new ProductA();
        }
    }
    
    // 具体工厂B
    public class ConcreteCreatorB extends Creator {
        @Override
        public Product factoryMethod() {
            return new ProductB();
        }
    }
    
    // 客户端代码
    public class Client {
        public static void main(String[] args) {
            Creator creator = new ConcreteCreatorA();
            Product product = creator.factoryMethod();
            product.doSomething();
        }
    }
    
  3. 抽象工厂模式(Abstract Factory): 抽象工厂模式在工厂方法模式的基础上进一步抽象,提供了一个接口,用于创建一系列相关或相互依赖的产品对象,而无需指定具体产品类。抽象工厂通常用于产品家族较大,且产品间有一定关联性的场景。

    优点

    • 可以创建一系列相关的产品对象,避免了将这些产品对象散布在各个地方。
    • 增加新的产品族时,不需要修改现有代码,符合开闭原则。

    缺点

    • 代码结构复杂,尤其是在产品家族较多时,增加了工厂类的复杂度。
    • 每新增一个产品族,就必须新增一个具体工厂类。

    示例代码:

    // 抽象产品A
    public interface ProductA {
        void doSomethingA();
    }
    
    // 具体产品A1
    public class ProductA1 implements ProductA {
        @Override
        public void doSomethingA() {
            System.out.println("Product A1 is doing something.");
        }
    }
    
    // 具体产品A2
    public class ProductA2 implements ProductA {
        @Override
        public void doSomethingA() {
            System.out.println("Product A2 is doing something.");
        }
    }
    
    // 抽象产品B
    public interface ProductB {
        void doSomethingB();
    }
    
    // 具体产品B1
    public class ProductB1 implements ProductB {
        @Override
        public void doSomethingB() {
            System.out.println("Product B1 is doing something.");
        }
    }
    
    // 具体产品B2
    public class ProductB2 implements ProductB {
        @Override
        public void doSomethingB() {
            System.out.println("Product B2 is doing something.");
        }
    }
    
    // 抽象工厂
    public interface AbstractFactory {
        ProductA createProductA();
        ProductB createProductB();
    }
    
    // 具体工厂1
    public class ConcreteFactory1 implements AbstractFactory {
        @Override
        public ProductA createProductA() {
            return new ProductA1();
        }
    
        @Override
        public ProductB createProductB() {
            return new ProductB1();
        }
    }
    
    // 具体工厂2
    public class ConcreteFactory2 implements AbstractFactory {
        @Override
        public ProductA createProductA() {
            return new ProductA2();
        }
    
        @Override
        public ProductB createProductB() {
            return new ProductB2();
        }
    }
    
    // 客户端代码
    public class Client {
        public static void main(String[] args) {
            AbstractFactory factory = new ConcreteFactory1();
            ProductA productA = factory.createProductA();
            productA.doSomethingA();
            ProductB productB = factory.createProductB();
            productB.doSomethingB();
        }
    }
    

3. 工厂模式+配置文件

在实际的系统设计中,工厂模式配置文件的结合使用,能够提高系统的灵活性与可扩展性,尤其是在需要根据外部配置动态创建不同对象的场景中。这种设计方式能够使得代码更加灵活、易于维护和扩展。

 工厂模式 + 配置文件的实现方式

假设我们需要根据配置来决定使用哪种数据库连接。可以使用工厂模式来封装数据库连接的创建逻辑,并通过配置类来读取外部的数据库类型配置,从而动态创建不同的数据库连接对象。

示例代码:

  • 定义产品接口(DatabaseConnection)
public interface DatabaseConnection {
    void connect();
}
  • 定义具体产品(MySQLConnection, PostgreSQLConnection)
public class MySQLConnection implements DatabaseConnection{
    @Override
    public void connect() {
        System.out.println("正在连接MySQL...");
    }
}
public class PostgreSQLConnection implements DatabaseConnection{
    @Override
    public void connect() {
        System.out.println("PostqreSQL正在连接中...");
    }
}
  • config.properties文件 
MySQL=com.study.factoryPattern.factoryAndConfigurationFile.MySQLConnection
PostgreSQL=com.study.factoryPattern.factoryAndConfigurationFile.PostgreSQLConnection
  • 工厂类(DatabaseConnectionFactory)
public class Factory {
    private static Map<String,DatabaseConnection> dataBaseConnections = new HashMap<>();

    static {
        //导入配置文件,并创建单例,储存在map中
        Properties properties = new Properties();
        InputStream resourceAsStream = Factory.class.getClassLoader().getResourceAsStream("config.properties");
        try {
            properties.load(resourceAsStream);
            Set<Object> keys = properties.keySet();
            for (Object key : keys) {
                String className = properties.getProperty((String) key);
                Class clazz = Class.forName(className);
                DatabaseConnection instance = (DatabaseConnection) clazz.newInstance();
                dataBaseConnections.put((String) key, instance);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static DatabaseConnection createConnection(String name){
        return dataBaseConnections.get(name);
    }
}
  • 客户端代码
public class Client {
    public static void main(String[] args) {
        // DatabaseConnection mySQL = Factory.createConnection("MySQL");
        DatabaseConnection mySQL = Factory.createConnection("PostgreSQL");
        mySQL.connect();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python 画图例子

目录 多组折线图点坐标的折线图 多组折线图 数据: 第1行为x轴标签第2/3/…行等为数据,其中第一列为标签&#xff0c;后面为y值 图片: 代码: import matplotlib.pyplot as plt# 原始数据字符串 # 第1行为x轴标签 # 第2/3/...行等为数据,其中第一列为标签&#xff0c;后面…

未来已来:少儿编程竞赛聚焦物联网,激发创新潜力

随着人工智能与物联网技术&#xff08;IoT&#xff09;的快速发展&#xff0c;少儿编程教育正在迎来新的变革浪潮。近年来&#xff0c;各类少儿编程竞赛纷纷增加了物联网相关主题&#xff0c;要求学生结合编程知识和硬件设备设计智能家居、智慧城市等创新项目。这一趋势不仅丰富…

Java-08 深入浅出 MyBatis - 多对多模型 SqlMapConfig 与 Mapper 详细讲解测试

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

字符串专题 算法小题

感觉很久不做题了, 本身自己虽然就没水平就是啦哈哈~ 那下面分享几道最近写的几道题, 都很简单, 是关于"字符串"的, 只不过会稍微用到一点代码能力就是了, 算是比较基础的题目. 目录 1.最长公共区域(⭐⭐⭐ 代码)1.1 题目描述1.2 题目思路方法1: 两两求公共区域方法2…

虚拟化的三种方式

1.前言 Virtualization(虚拟化)是让公开的虚拟资源等同于被虚拟化的底层物理资源。虚拟化在各个领域应用很广泛&#xff0c;不局限于计算机科学领域。无论是在硬件、软件还是在嵌入式子系统中&#xff0c;虚拟化总是使用或组合三种简单的技术来实现的&#xff1a;多路复用(Mul…

使用yolov5查看模式标注情况

import cv2 from ultralytics import YOLO# 加载模型 model YOLO(E:\\yolov\\yolov9\\runs\\detect\\train4\\weights\\best.pt) # 替换为您的模型路径# 读取视频文件 cap cv2.VideoCapture(5.mp4) # 替换为您的视频文件路径# 定义输出视频的编码器和创建VideoWriter对象 f…

Rust 力扣 - 198. 打家劫舍

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 假设f(i)表示在[1, i]号内的房屋内进行偷盗能够获得的最高金额 存在递推公式 f(i) max(f(i - 1), f(i - 2) nums[i]) 即f(i)为选择i - 1号房屋的最大金额 和 选择i - 2号房屋的最大金额 的最大值 题解代码 …

Redis持久化、主从及哨兵架构详解

Redis持久化 RDB快照&#xff08;snapshot&#xff09; 在默认情况下&#xff0c;Redis将内存数据库快照保存在名字为dump.rdb的二进制文件中。 你可以对Redis进行设置&#xff0c;让它在“N秒内数据集至少有M个改动”这一条件被满足时&#xff0c;自动保存一次数据集。 比…

解决启动Tomcat时出现的乱码问题

日志乱码 日志乱码就是启动Tomcat时红色的字体出现乱码&#xff08;下图没有乱码&#xff09;。 解决方案 &#xff1a; 找到Tomcat的安装目录&#xff0c;点进conf目录 点进logging.properties文件 找到java.util.logging.ConsoleHandler.encoding字段&#xff0c;修改成GBK…

网络爬虫——常见问题与调试技巧

在开发网络爬虫的过程中&#xff0c;开发者常常会遇到各种问题&#xff0c;例如网页加载失败、数据提取错误、反爬机制限制等。以下内容将结合实际经验和技术方案&#xff0c;详细介绍解决常见错误的方法&#xff0c;以及如何高效调试和优化爬虫代码。 1. 爬虫过程中常见的错误…

初识Linux(3):Linux基础环境工具(上)

目录 1. yum 1.1 软件的生态 1.2 yum使用 2. vim 4. vim三种模式的更详细命令 5. gcc 6. 重要概念&#xff1a;函数库 7. 动态库与静态库 8. 自动化构建工具&#xff1a;make/Makefile .PHONY 9. make 与 是否执行 %通识符 生成两个可执行程序 10.练习 &#xff…

负载均衡在线OJ项目

OnlineJudge 前言所用技术开发环境 1. 需求分析2. 项目宏观结构3. compile_server服务设计3.1 compiler服务设计3.2 runner服务设计3.3 compile_run3.4 compile_server.cpp 4. oj_server服务设计4.1 model设计4.2 view设计4.3 control设计4.3.1 获取题目列表功能4.3.2 获取单个…

Kafka 分区分配及再平衡策略深度解析与消费者事务和数据积压的简单介绍

Kafka&#xff1a;分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析&#xff1a;从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…

使用argo workflow 实现springboot 项目的CI、CD

文章目录 基础镜像制作基础镜像设置镜像源并安装工具git下载和安装 Maven设置环境变量设置工作目录默认命令最终dockerfile 制作ci argo workflow 模版volumeClaimTemplatestemplatesvolumes完整workflow文件 制作cd argo workflow 模版Workflow 结构Templates 定义创建 Kubern…

进程间通信--详解

目录 前言一、进程间通信介绍1、进程间通信目的2、进程间通信发展3、进程间通信的分类4、进程间通信的必要性5、进程间通信的技术背景6、进程间通信的本质理解 二、管道1、什么是管道2、匿名管道pipe&#xff08;1&#xff09;匿名管道的原理&#xff08;2&#xff09;pipe函数…

【虚拟机】VMWare的CentOS虚拟机断电或强制关机出现问题

VMware 虚拟机因为笔记本突然断电故障了&#xff0c;开机提示“Entering emergency mode. Exit the shell to continue.”&#xff0c;如下图所示&#xff1a; 解决方法&#xff1a;输入命令&#xff1a; xfs_repair -v -L /dev/dm-0 注&#xff1a;报 no such file or direct…

FinalShell进行前端项目部署及nginx配置

首先需要准备服务器(阿里云、腾讯云都可)与域名&#xff1b; 示例为阿里云服务器&#xff1b; 1.进行FinalShell下载 下载官网 https://www.hostbuf.com/ 2.下载完毕后 配置FinalShell ssh ​ 名称自定义即可&#xff01; 2-1 提示连接成功 ​ 3.首先检查nginx是否下载 …

[RabbitMQ] 重试机制+TTL+死信队列

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

(附项目源码)Java开发语言,220 ssm电影推荐系统的分析与设计,计算机毕设程序开发+文案(LW+PPT)

目 录 摘 要 Abstract 第1章 前 言 1.1 研究背景 1.2 研究现状 1.3 系统开发目标 第2章 技术与原理 2.1 开发技术 2 2.2 ssm框架介绍 2 2.3 MySQL数据库 2 2.4 B/S结构 2 第3章 需求分析 3.1 需求分析 3.2 系统可行性分析 3.3 项目设计目标与原则 3.4…

--- 文件IO java ---

文本文件和二进制文件 文件再底层其实就是以一段二进制数据的形式储存的&#xff0c;当我用记事本打开文件时&#xff0c;有些文件会出现乱码&#xff0c;这就是二进制文件&#xff0c;而有一些文件是特殊的&#xff0c;他以特定的编码方式&#xff08;比如ascll&#xff09;可…