设计模式9-工厂模式(Factory Method)

news2024/11/24 15:57:09

@[TOC](工厂模式(Factory Method))

写在前面

对象创建模式

通过对象超级模式绕开。动态内存分配(new),来避免对象创建过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定,它是结构抽象之后的第一步工作。

典型模式:

  • 工厂方法(Factory Method Pattern)
  • 抽象工厂(Abstract Factory Pattern)
    提供一个接口,用于创建相关或依赖对象的家族,而不需要指定具体的类。
  • 原型模式(Prototype Pattern)
    将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
  • 构建器(Builder Pattern)
    通过复制现有对象来创建新对象,而不是通过实例化类来创建新对象

对象创建模式(Creational Patterns)是设计模式的一类,主要关注对象的创建过程。它们提供了一种在系统中创建对象的最佳方法,而不是通过直接实例化类的方式。

动机

在软件系统中经常面临着创建对象的工作,由于需求的变化,需要创建的对象的具体类型经常发生变化。

但是如何应对这种变化呢?如何绕过常规的对象创建方法,提供一种封装机制来避免客户程序和这种具体对象创建工作的紧耦合?

那么工厂模式就是为了解决这类问题。

代码推导

class {
public:
    virtual void split()=0;
    virtual ~ISplitter(){}
};

class BinarySplitter : public ISplitter{
    
};

class TxtSplitter: public ISplitter{
    
};

class PictureSplitter: public ISplitter{
    
};

class VideoSplitter: public ISplitter{
    
};


class MainForm : public Form
{
public:
	void Button1_Click(){
		ISplitter * splitter=
            new BinarySplitter();//依赖具体类
        
        splitter->split();

	}
};

这段代码展示了一个简单的面向对象设计,其中包含了一个用于演示不同分割器的类结构。具体类 MainForm 依赖于具体的分割器类,展示了如何使用这些类来进行操作。

1. 抽象类 ISplitter

class ISplitter {
public:
    virtual void split() = 0;
    virtual ~ISplitter() {}
};
  • ISplitter 是一个抽象类(接口类),定义了一个纯虚函数 split(),表示分割器对象需要实现的行为。
  • 纯虚函数(= 0)使得 ISplitter 成为抽象类,不能直接实例化,只能通过继承来实现。
  • 虚析构函数 ~ISplitter() 确保派生类对象被正确销毁。

2. 具体的分割器类

class BinarySplitter : public ISplitter {
public:
    void split() override {
        // 实现二进制文件的分割
    }
};

class TxtSplitter : public ISplitter {
public:
    void split() override {
        // 实现文本文件的分割
    }
};

class PictureSplitter : public ISplitter {
public:
    void split() override {
        // 实现图片文件的分割
    }
};

class VideoSplitter : public ISplitter {
public:
    void split() override {
        // 实现视频文件的分割
    }
};
  • BinarySplitterTxtSplitterPictureSplitterVideoSplitterISplitter 的具体实现类,每个类都实现了 split() 方法,用于处理不同类型的文件分割。

3. 主窗体类 MainForm

class MainForm : public Form {
public:
    void Button1_Click() {
        ISplitter *splitter = new BinarySplitter(); // 依赖具体类
        splitter->split();
        delete splitter; // 不要忘记释放内存
    }
};
  • MainForm 继承自 Form 类(假设 Form 类定义了 GUI 窗体的基本功能)。
  • Button1_Click() 方法模拟了一个按钮点击事件,在这个事件中创建了一个 BinarySplitter 对象并调用其 split() 方法进行分割操作。
  • delete splitter 用于释放内存,防止内存泄漏。

代码的缺陷

  1. 硬编码依赖: MainForm 类中硬编码了具体的 BinarySplitter 类,这导致代码的灵活性和可扩展性差。如果需要更换分割器类型,需要修改 MainForm 的代码。
  2. 违反开闭原则: 如果需要添加新的分割器类型,需要修改现有代码,这违反了开闭原则(对扩展开放,对修改关闭)。
  3. 缺少内存管理: 虽然 delete splitter 释放了内存,但如果 split() 方法中抛出异常,会导致内存泄漏。因此,推荐使用智能指针来自动管理内存。

从上面的代码可以看出,Button1_Click函数中的ISplitter 指针需要依赖new出具体的类。来调用split函数。那么问题来了,如何绕开new? 就是我们需要一个接口创建具体的类。而且这个接口也是动态的,也就是虚函数。能实现代码中的依赖倒置原则中的具体依赖抽象。但是此时我们不可能再基类中拥有这个结构。

直接在 ISplitter 中声明 CreateSplitter 接口会引入一些设计上的问题和违反面向对象设计的原则。下面是一些关键原因:

  1. 职责单一原则(Single Responsibility Principle, SRP)

    • ISplitter 的职责应该是定义分割操作的接口,而不是负责创建分割器实例。将创建逻辑和分割逻辑混合在同一个接口中,会导致接口的职责过多,不符合职责单一原则。
  2. 接口隔离原则(Interface Segregation Principle, ISP)

    • ISplitter 接口的用户可能只需要调用 split 方法,而不关心如何创建分割器实例。如果将创建方法添加到 ISplitter 中,所有实现 ISplitter 的类都必须实现这个方法,这会增加实现的复杂性,并且违反接口隔离原则。
  3. 工厂模式的作用

    • 工厂模式旨在封装对象创建的过程,使得创建逻辑与使用逻辑分离。如果将创建逻辑直接放在 ISplitter 接口中,就无法利用工厂模式的优势,例如替换具体实现、延迟实例化等。
  4. 可扩展性和灵活性

    • 通过将创建逻辑放在工厂类中,可以在不修改现有代码的情况下添加新的分割器类型。这有助于遵循开闭原则(对扩展开放,对修改关闭)。如果将创建逻辑放在 ISplitter 中,添加新的分割器类型就需要修改接口及其所有实现,降低了代码的可扩展性。

不推荐的设计:将创建方法放在 ISplitter

class ISplitter {
public:
    virtual void split() = 0;
    virtual ISplitter* CreateSplitter() = 0;
    virtual ~ISplitter() {}
};

class BinarySplitter : public ISplitter {
public:
    void split() override {
        // 实现二进制文件的分割
    }
    ISplitter* CreateSplitter() override {
        return new BinarySplitter();
    }
};

// MainForm 依赖具体实现,违背单一职责原则和工厂模式的初衷
class MainForm : public Form {
public:
    void Button1_Click() {
        ISplitter *splitter = new BinarySplitter(); // 或调用 splitter->CreateSplitter()
        splitter->split();
        delete splitter;
    }
};

此时需要新建一个接口基类,还存放CreateSplitter函数,此时就要用到工厂模式。

改进建议

可以使用工厂模式或依赖注入来解决上述缺陷,使得 MainForm 不依赖具体的分割器类,从而提高代码的灵活性和可扩展性。例如:

使用工厂模式改进
class SplitterFactory {
public:
    virtual ISplitter* CreateSplitter() = 0;
    virtual ~SplitterFactory() {}
};

class BinarySplitterFactory : public SplitterFactory {
public:
    ISplitter* CreateSplitter() override {
        return new BinarySplitter();
    }
};

class MainForm : public Form {
private:
    SplitterFactory *factory;

public:
    MainForm(SplitterFactory *f) : factory(f) {}

    void Button1_Click() {
        ISplitter *splitter = factory->CreateSplitter();
        splitter->split();
        delete splitter;
    }
};

通过使用 SplitterFactory,可以在运行时决定创建哪种类型的分割器,而不需要修改 MainForm 的代码,提高了系统的灵活性和可维护性。

模式定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂模式使得一个类的实例化得到了延迟到子类中。这样使用虚函数的方式目的在于能够解耦和。

模式结构

简单工厂模式

    +-----------------+
    |  SimpleFactory  |
    +-----------------+
    |  + createProduct(type: String): Product  |
    +-----------------+
              |
              |
      +-------+-------+
      |               |
      v               v
+----------------+ +----------------+
| ConcreteProductA | | ConcreteProductB |
+----------------+ +----------------+
|  + use()       | |  + use()       |
+----------------+ +----------------+
       ^                ^
       |                |
       +--------+-------+
                |
              +------+
              |Product|
              +------+
              |+use() |
              +------+

工厂方法模式

    +-----------------+
    |    Factory      |
    +-----------------+
    |  + createProduct() : Product  |
    +-----------------+
              |
              |
      +-------+-------+
      |               |
      v               v
+----------------+ +----------------+
|ConcreteFactoryA| |ConcreteFactoryB|
+----------------+ +----------------+
|+createProduct(): Product|+createProduct(): Product|
+----------------+ +----------------+
      |                |
      v                v
+----------------+ +----------------+
| ConcreteProductA | | ConcreteProductB |
+----------------+ +----------------+
|  + use()       | |  + use()       |
+----------------+ +----------------+
       ^                ^
       |                |
       +--------+-------+
                |
              +------+
              |Product|
              +------+
              |+use() |
              +------+

抽象工厂模式

        +--------------------+
        |  AbstractFactory   |
        +--------------------+
        |  + createProductA(): ProductA |
        |  + createProductB(): ProductB |
        +--------------------+
              |
      +-------+-------+
      |               |
      v               v
+----------------+ +----------------+
| ConcreteFactory1| | ConcreteFactory2|
+----------------+ +----------------+
|+createProductA(): ProductA|+createProductA(): ProductA|
|+createProductB(): ProductB|+createProductB(): ProductB|
+----------------+ +----------------+
      |                |
      |                |
      v                v
+----------------+ +----------------+
| ConcreteProductA1 | | ConcreteProductA2 |
+----------------+ +----------------+
|  + use()       | |  + use()       |
+----------------+ +----------------+
       ^                ^
       |                |
       +--------+-------+
                |
              +------+
              |ProductA|
              +------+
              |+use() |
              +------+
      |                |
      v                v
+----------------+ +----------------+
| ConcreteProductB1 | | ConcreteProductB2 |
+----------------+ +----------------+
|  + use()       | |  + use()       |
+----------------+ +----------------+
       ^                ^
       |                |
       +--------+-------+
                |
              +------+
              |ProductB|
              +------+
              |+use() |
              +------+

在这里插入图片描述

这些图示展示了各个设计模式的主要组件及其关系,希望能帮助你更好地理解工厂模式的结构。

工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的方式,而无需在代码中显式指定要创建的具体类。工厂模式可以分为简单工厂模式、工厂方法模式和抽象工厂模式。下面分别介绍这些模式的结构。

简单工厂模式

简单工厂模式通常也被称为静态工厂方法(Static Factory Method)模式,它通过一个工厂类来创建对象。这个工厂类包含一个静态方法,根据输入的参数来决定创建哪种具体类的实例。

结构:

  1. 工厂类(Factory):包含一个静态方法,用于根据不同的条件创建不同的对象。
  2. 产品类(Product):工厂创建的对象的父类或接口。
  3. 具体产品类(ConcreteProduct):工厂类实际创建的具体对象。

工厂方法模式

工厂方法模式通过定义一个创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

结构:

  1. 抽象工厂类(Factory):声明一个创建产品对象的工厂方法,返回产品的实例。
  2. 具体工厂类(ConcreteFactory):实现创建产品对象的工厂方法。
  3. 产品类(Product):工厂创建的对象的父类或接口。
  4. 具体产品类(ConcreteProduct):工厂类实际创建的具体对象。

旨在将对象的创建过程封装起来,使得客户端代码可以依赖于接口而不是具体的实现类,从而提高代码的可维护性和可扩展性。

要点总结

  • 工厂方法模式用于隔离类对象的使用者和使用类型之间的耦合关系。面对一个经常变化的具体类型。紧耦合关系会导致软件的脆弱。
  • 工厂方法模式,通过面向对象的手法将所要创建的具体对象工作延时到此类,从而实现一种扩展而非更改的策略,较好地解决了这种紧耦合的关系。
  • 工厂方法模式解决单个对象的需求变化。缺点在于要求创建方法的参数相同。

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

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

相关文章

新版本安卓更换下载源解决gradle时间太久问题

老版本android studio 解决方法如下 : android studio gradle:build model执行时间太久 最近又做到安卓的任务了,下载的安卓studio最新版 这个版本的android studio 不能用上面那种老版本的方法了,需要更新方法 新版本需要跟换两个地方 gradle/wrapper/gradle-wrapper.proper…

JAVA 异步编程(异步,线程,线程池)一

目录 1.概念 1.1 线程和进程的区别 1.2 线程的五种状态 1.3 单线程,多线程,线程池 1.4 异步与多线程的概念 2. 实现异步的方式 2.1 方式1 裸线程(Thread) 2.1 方式2 线程池(Executor) 2.1.1 源码分析 2.1.2 线程池创建…

三丰云评测:免费虚拟主机与免费云服务器体验

今天我来为大家分享一下我对三丰云的评测。作为一家知名的云服务提供商,三丰云一直以来备受用户好评。他们提供免费虚拟主机和免费云服务器服务,深受网站建设者和开发者的喜爱。 首先谈谈免费虚拟主机服务。三丰云的免费虚拟主机方案性价比非常高&#x…

代码随想录算法训练营第33天|LeetCode 509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

1. LeetCode 509. 斐波那契数 题目链接:https://leetcode.cn/problems/fibonacci-number/ 文章链接:https://programmercarl.com/0509.斐波那契数.html 视频链接:https://www.bilibili.com/video/BV1f5411K7mo 思路: 动态规划步骤…

django-ckeditor富文本编辑器

一.安装django-ckeditor 1.安装 pip install django-ckeditor2.注册应用 INSTALLED_APPS [...ckeditor, ]3.配置model from ckeditor.fields import RichTextFieldcontent RichTextField()4.在项目中manage.py文件下重新执行迁移,生成迁移文件 py…

jvm优化

1.jvm组成 什么是jvm,java是跨平台语言,对不同的平台(windos,linux),有不同的jvm版本。jvm屏蔽了平台的不同,提供了统一的运行环境,让java代码无需考虑平台的差异。 jdk包含jre包含…

ValueError和KeyError: ‘bluegrass’的问题解决

项目场景: 项目相关背景: 问题描述 遇到的问题1: KeyError: ‘bluegrass’ 不能识别某标签 遇到的问题2: xml etree.fromstring(xml_str) ValueError: Unicode strings with encoding declaration are not supported. Please …

K8S POD控制器:从基础到高级实战技巧

一、引言 在当今的云计算时代,Kubernetes(K8s)已成为最受欢迎的容器编排工具,它的核心组成部分之一——K8s POD控制器,扮演着至关重要的角色。这篇文章旨在深入探讨K8s POD控制器的内部工作原理、不同类型及其应用场景…

【数据结构】树和二叉树及堆的深入理解

【数据结构】树和二叉树及堆的深入理解 🔥个人主页:大白的编程日记 🔥专栏:数据结构 文章目录 【数据结构】树和二叉树及堆的深入理解前言一.树1.1 树的概念1.2 树的相关概念1.3 树的表示1.4 树的应用 二.二叉树2.1 二叉树概念及…

clion中建立c文件工程,读取或创建sqlite3数据库文件

1.首先前往SQLite官网下载sqlite3所需文件 SQLite Download Page 2.解压文件,将其中的sqlite3.c和sqlite3.h拷贝到你对应的文件工程中 3.修改CMakeLists.txt文件,添加编译选项及连接文件 4.运行代码及查询数据库文件

实战:SpringBoot 15个功能强大Bean

下面这15个bean,可以很方便的帮我们获取当前环境信息,运行信息,参数信息等等 1. 应用程序参数Environment和ApplicationArguments SpringBoot程序在启动时,可以通过如下方式设置启动参数: java -jar app.jar --pack…

单链表算法 - 链表分割

链表分割_牛客题霸_牛客网现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的。题目来自【牛客题霸】https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70思路: 代码: /* struct ListNode {int val;struct List…

iterator(迭代器模式)

引入 在想显示数组当中所有元素时&#xff0c;我们往往会使用下面的for循环语句来遍历数组 #include <iostream> #include <vector>int main() {std::vector<int> v({ 1, 2, 3 });for (int i 0; i < v.size(); i){std::cout << v[i] << &q…

正则表达式(Ⅲ)——分组匹配

简介 为了给表达式分组&#xff0c;我们需要将文本包裹在 () 中 有点类似于匹配子串&#xff0c;只不过是找出所有的子串&#xff0c;并且拼成一组 分组之间需要有分割符&#xff0c;,或者-或者_都可以 直接分组 引用分组 这个比较难以理解 \1和\2的作用有两个&#xff1a…

Ubuntu系统修改SSH默认端口号

1.查看系统和系统版本号 rootecs-c0fe:~# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04.7 LTS Release: 16.04 Codename: xenial rootecs-c0fe:~# uname -a Linux ecs-c0fe 4.4.0-210-generic #242-Ubunt…

Python入门------多个版本--虚拟环境的创建(非anaconda方式)

说明介绍&#xff1a; 1. 什么是虚拟环境 在Python中&#xff0c;虚拟环境&#xff08;Virtual Environment&#xff09;是一个独立的、隔离的Python运行环境&#xff0c;它拥有自己的Python解释器、第三方库和应用程序。通过创建虚拟环境&#xff0c;可以确保项目之间的依赖关…

Python 实验五 高级数据结构

一、实验目的 &#xff08;1&#xff09;掌握序列的基本操作 &#xff08;2&#xff09;掌握集合、字典的基本操作 二、实验环境 联网计算机一台/每人&#xff0c;内装Windows 7以上操作系统和安装Python 3.7集成开发环境IDLE。 三、实验内容 Sy5-1 列表实现。编写一个…

数据库的约束条件和用户管理

约束条件&#xff1a; 主键&#xff1a;主键约束 primary key 用于标识表中的主键列的值&#xff0c;而且这个值是全表当中唯一的&#xff0c;而且只不能为null 一个表只能有一个主键。 外键&#xff1a;用来建立表与表之间的关系。确保外键中的值于另一个表的主键值匹配&a…

camtasia怎么剪掉不用的部分 屏幕录制的视频怎么裁剪上下不要的部分 camtasia studio怎么裁剪视频时长 camtasia怎么剪辑视频教程

有时我们录制的屏幕内容&#xff0c;并不一定全部需要。那么&#xff0c;屏幕录制的视频怎么裁剪上下不要的部分&#xff1f;可以使用视频剪辑软件&#xff0c;或者微课制作工具来进行裁剪。屏幕录制的视频怎么旋转&#xff1f;录制视频的旋转也是一样的&#xff0c;均在编辑步…

可视化工具选择指南:助力企业数字化转型和新质生产力发展

随着信息技术的快速发展和新质生产力概念的兴起&#xff0c;可视化工具在各个行业中的作用日益凸显。这些工具不仅能够帮助用户更直观地理解和分析数据&#xff0c;还能提升团队的协作效率和决策质量。 在当今数字化转型迅速发展的背景下&#xff0c;新质生产力的概念正在成为…