设计模式——工厂模式

2023-05-16

工厂方法模式

  • 1 前言
  • 2 简介
  • 3 概念示例
  • 4 应用示例
  • 5 适用场景
  • 6 实现方式
  • 7 工厂方法模式优缺点
  • 8 与其它模式的关系


1 前言

工厂模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

2 简介

意图:
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

主要解决:
主要解决接口选择的问题。

何时使用:
我们明确地计划不同条件下创建不同实例时。

如何解决:
让其子类实现工厂接口,返回的也是一个抽象的产品。

关键代码:
创建过程在其子类执行。

注意事项:
作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

3 概念示例

本例说明了工厂方法设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

在这里插入图片描述

from __future__ import annotations
from abc import ABC, abstractmethod


class Creator(ABC):
    """
   造物主类声明的工厂方法应该返回一个   
   一个产品类的对象。通常创造者的子类提供 
   该方法的实现。
    """

    @abstractmethod
    def factory_method(self):
        """
         请注意,创作者也可以提供一些默认的实现   
         工厂方法。
        """
        pass

    def some_operation(self) -> str:
        """
       还要注意,尽管它的名字,创建者的首要责任   
       不是创造产品。通常,它包含一些核心业务逻辑   
       依靠产品对象,返回的工厂方法。   
       子类可以间接地改变业务逻辑通过重写   
       工厂方法返回一个不同类型的产品。
        """

        # Call the factory method to create a Product object.
        product = self.factory_method()

        # Now, use the product.
        result = f"Creator: The same creator's code has just worked with {product.operation()}"

        return result


"""
具体的创建者覆盖工厂方法为了改变结果   
产品的类型。
"""

class ConcreteCreator1(Creator):
    """
    注意,该方法的签名仍然使用抽象的产品类型,   
    尽管具体的产品实际上是返回的方法。这   
    创作者可以保持独立于具体产品类。
    """

    def factory_method(self) -> Product:
        return ConcreteProduct1()


class ConcreteCreator2(Creator):
    def factory_method(self) -> Product:
        return ConcreteProduct2()


class Product(ABC):
    """
   产品接口声明的操作,所有具体的产品   
   必须实现。
    """

    @abstractmethod
    def operation(self) -> str:
        pass


"""
具体的产品提供各种产品接口的实现。
"""


class ConcreteProduct1(Product):
    def operation(self) -> str:
        return "{Result of the ConcreteProduct1}"


class ConcreteProduct2(Product):
    def operation(self) -> str:
        return "{Result of the ConcreteProduct2}"


def client_code(creator: Creator) -> None:
    """
   客户端代码与一个具体创建者的实例一起工作,尽管是通过它的基本接口。只要客户端通过基本接口继续使用创建者,就可以传递任何创建者的子类。
    """

    print(f"Client: I'm not aware of the creator's class, but it still works.\n"
          f"{creator.some_operation()}", end="")


if __name__ == "__main__":
    print("App: Launched with the ConcreteCreator1.")
    client_code(ConcreteCreator1())
    print("\n")

    print("App: Launched with the ConcreteCreator2.")
    client_code(ConcreteCreator2())

4 应用示例

1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
2、Hibernate 换数据库只需换方言和驱动就可以。
工厂方法将创建对象的工作让相应的工厂子类去实现,保证在新增工厂类时,不用修改原有代码,首先,创建一个抽象公共工厂类,并定义一个生产对象的方法,接着,创建抽象工厂类的 3 个子类,并重写方法,创建一个实例对象并返回

import abc
from factory.fruit import *

class AbstractFactory(object):
    """抽象工厂"""
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def get_fruit(self):
        pass
class AppleFactory(AbstractFactory):
    """生产苹果"""

    def get_fruit(self):
        return Apple()

class BananaFactory(AbstractFactory):
    """生产香蕉"""

    def get_fruit(self):
        return Banana()

class OrangeFactory(AbstractFactory):
    """生产橘子"""

    def get_fruit(self):
        return Orange()

if __name__ == '__main__':
    # 每个工厂负责生产自己的产品也避免了我们在新增产品时需要修改工厂的代码,而只要增加相应的工厂即可
    instance_apple = AppleFactory().get_fruit()
    instance_banana = BananaFactory().get_fruit()
    instance_orange = OrangeFactory().get_fruit()

    print(instance_apple)
    print(instance_banana)
    print(instance_orange)

5 适用场景

1. 当你在编写代码的过程中, 如果无法预知对象确切类别及其依赖关系时, 可使用工厂方法。

工厂方法将创建产品的代码与实际使用产品的代码分离, 从而能在不影响其他代码的情况下扩展产品创建部分代码。

例如, 如果需要向应用中添加一种新产品, 你只需要开发新的创建者子类, 然后重写其工厂方法即可。

2. 如果你希望用户能扩展你软件库或框架的内部组件, 可使用工厂方法。

继承可能是扩展软件库或框架默认行为的最简单方法。 但是当你使用子类替代标准组件时, 框架如何辨识出该子类?

解决方案是将各框架中构造组件的代码集中到单个工厂方法中, 并在继承该组件之外允许任何人对该方法进行重写。

让我们看看具体是如何实现的。 假设你使用开源 UI 框架编写自己的应用。 你希望在应用中使用圆形按钮, 但是原框架仅支持矩形按钮。 你可以使用 圆形按钮Round­Button子类来继承标准的 按钮Button类。 但是, 你需要告诉 UI框架UIFramework类使用新的子类按钮代替默认按钮。

为了实现这个功能, 你可以根据基础框架类开发子类 圆形按钮 UIUIWith­Round­Buttons , 并且重写其 create­Button创建按钮方法。 基类中的该方法返回 按钮对象, 而你开发的子类返回 圆形按钮对象。 现在, 你就可以使用 圆形按钮 UI类代替 UI框架类。 就是这么简单!

3. 如果你希望复用现有对象来节省系统资源, 而不是每次都重新创建对象, 可使用工厂方法。

在处理大型资源密集型对象 (比如数据库连接、 文件系统和网络资源) 时, 你会经常碰到这种资源需求。

让我们思考复用现有对象的方法:

  1. 首先, 你需要创建存储空间来存放所有已经创建的对象。
  2. 当他人请求一个对象时, 程序将在对象池中搜索可用对象。
  3. …然后将其返回给客户端代码。
  4. 如果没有可用对象, 程序则创建一个新对象 (并将其添加到对象池中)。

这些代码可不少!而且它们必须位于同一处, 这样才能确保重复代码不会污染程序。

可能最显而易见, 也是最方便的方式, 就是将这些代码放置在我们试图重用的对象类的构造函数中。 但是从定义上来讲, 构造函数始终返回的是新对象, 其无法返回现有实例。

因此, 你需要有一个既能够创建新对象, 又可以重用现有对象的普通方法。 这听上去和工厂方法非常相像。

4. 举例:

  • 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
  • 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
  • 3、设计一个连接服务器的框架,需要三个议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。

6 实现方式

  • 让所有产品都遵循同一接口。 该接口必须声明对所有产品都有意义的方法。

  • 在创建类中添加一个空的工厂方法。 该方法的返回类型必须遵循通用的产品接口。

  • 在创建者代码中找到对于产品构造函数的所有引用。 将它们依次替换为对于工厂方法的调用, 同时将创建产品的代码移入工厂方法。 你可能需要在工厂方法中添加临时参数来控制返回的产品类型。

  • 工厂方法的代码看上去可能非常糟糕。 其中可能会有复杂的 switch分支运算符, 用于选择各种需要实例化的产品类。 但是不要担心,我们很快就会修复这个问题。

  • 现在, 为工厂方法中的每种产品编写一个创建者子类, 然后在子类中重写工厂方法, 并将基本方法中的相关创建代码移动到工厂方法中。

  • 如果应用中的产品类型太多, 那么为每个产品创建子类并无太大必要, 这时你也可以在子类中复用基类中的控制参数。

例如, 设想你有以下一些层次结构的类。 基类 邮件及其子类 航空邮件和 陆路邮件 ; ​ 运输及其子类 飞机, 卡车和 火车 。 ​ 航空邮件仅使用 飞机对象, 而 陆路邮件则会同时使用 卡车和 火车对象。 你可以编写一个新的子类 (例如 火车邮件 ) 来处理这两种情况, 但是还有其他可选的方案。 客户端代码可以给 陆路邮件类传递一个参数, 用于控制其希望获得的产品。

  • 如果代码经过上述移动后, 基础工厂方法中已经没有任何代码, 你可以将其转变为抽象类。 如果基础工厂方法中还有其他语句, 你可以将其设置为该方法的默认行为。

7 工厂方法模式优缺点

优点:

  • 你可以避免创建者和具体产品之间的紧密耦合。
  • 单一职责原则。 你可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护。
  • 开闭原则。 无需更改现有客户端代码, 你就可以在程序中引入新的产品类型。

1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:

  • 应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中。

8 与其它模式的关系

  • 在许多设计工作的初期都会使用工厂方法模式 (较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂模式、 原型模式或生成器模式 (更灵活但更加复杂)。

  • 抽象工厂模式通常基于一组工厂方法, 但你也可以使用原型模式来生成这些类的方法。

  • 你可以同时使用工厂方法和迭代器模式来让子类集合返回不同类型的迭代器, 并使得迭代器与集合相匹配。

  • 原型并不基于继承, 因此没有继承的缺点。 另一方面, 原型需要对被复制对象进行复杂的初始化。 工厂方法基于继承, 但是它不需要初始化步骤。

  • 工厂方法是模板方法模式的一种特殊形式。 同时, 工厂方法可以作为一个大型模板方法中的一个步骤。

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

设计模式——工厂模式 的相关文章

  • PyQt——QStackedWidget

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQt5中 QStackedWidge
  • PyQt——QDockWidget

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQt5中 QDockWidget 例
  • lingo入门教程之二 --- 集合运用

    lingo中的集合用法很多 xff0c 这里主要通过几个例题来进行讲解 对于每一个问题 xff0c 都要先找到对应的目标函数 xff0c 然后对相应值进行初始化 xff0c 然后找到约束条件等进行求解 例1 xff1a SAILCO公司需要
  • PyQt——MDI(多文档界面)

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQt5中 QMdiArea 例子 3
  • PyQt——QScrollBar

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQt5中 QScrollBar 例子
  • PyQt——QTimer

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQT5中 QTimer例子 39 3
  • PyQt——QThread

    span class token comment coding utf 8 span span class token triple quoted string string 39 39 39 简介 PyQT5中 QThread 例子 39
  • PyQt——事件处理

    PyQt事件处理 前言1 低级的事件处理程序2 事件处理机制1 事件机制与信号槽机制的区别2 事件处理的方法3 事件处理实例 前言 1 低级的事件处理程序 span class token comment coding utf 8 span
  • python——精华技巧

    1 大量的if else结构采用字典key来匹配 days span class token operator 61 span span class token string 34 Mon Tue Wed Thu Fri Sat Sun 3
  • Python使用struct处理二进制(pack和unpack用法)

    python有时需要处理二进制数据 xff0c 例如 存取文件 xff0c socket操作时 这时候 xff0c 可以使用python的struct模块来完成 可以用struct来处理c语言中的结构体 一 struct模块中最重要的三个函
  • PyQt——QGroupBox

    QGroupBox为构建分组框提供了支持 分组框通常带有一个边框和一个标题栏 xff0c 作为容器部件来使用 xff0c 在其中可以布置各种窗口部件 布局时可用作一组控件的容器 xff0c 但是需要注意的是 xff0c 内部必须使用布局控件
  • PyQt——高清屏幕自适应设置

    Qt Designer 设计界面 xff1a 在高清屏未设置AA EnableHighDpiScaling的预览界面 xff1a 布局字体控件尺寸上明显存在偏差 设置了AA EnableHighDpiScaling的预览界面 xff1a 运
  • PyQt——实现多窗口

    主窗口通过按钮显示子窗口 1 使用qtdesigner设计窗口 主窗口 xff1a main window 子窗口 xff1a Dialog 生成的是ui文件 2 为两个窗口生成py文件 3 创建两个窗口的继承类 span class to
  • 《统计学习方法》各章节代码实现与课后习题参考解答

    待完成 统计学习方法 各章节代码实现与课后习题参考解答 章节代码课后习题第1章 统计学习方法概论 LeastSquaresMethod 传送门传送门 第2章 感知机 Perceptron 传送门传送门第3章 k近邻法 KNearestNei
  • Opencv的鼠标事件

    span class token keyword import span cv2 span class token keyword import span numpy span class token keyword as span np
  • python多线程之事件触发(线程间通信)

    span class token keyword import span threading event span class token operator 61 span threading span class token punctu
  • 架构规范

    系统架构规范 目录一 架构规范二 数据库规范三 SOA规范四 安全规范 目录 一 架构规范 所有的输入参数需要做合法性检验 xff1b 不允许出现空指针异常和数组越界异常 xff1b 不允许出现不受控制的大对象 xff0c 如没有限定大小的
  • 非主线程(GUI线程)中实现弹窗

    非主线程 xff08 GUI线程 xff09 中实现弹窗 目录一 在非主线程 xff08 GUI线程 xff09 中实现弹窗Step 1 创建界面文件 ui run loop dialog py Step 2 xff1a 代码主体 run
  • PyQT——多线程(QThread)

    PyQT线程 xff1a 多线程QThread 前言一 应用场景二 使用多线程解决卡顿和假死第一种 xff1a 线程锁 QMutex 第二种 xff1a 信号 Signal 前言 一 应用场景 在编写GUI界面中 xff0c 通常用会有一些
  • OpenCV-Python ——图像加文字

    Opencv 视频添加文字和标记框 前言一 OpenCV添加文字问题二 解决思路1 OpenCV图片转换为PIL图片格式2 使用PIL绘制文字3 PIL图片格式转换成OpenCV的图片格式 三 完整代码 前言 最近做了个视屏实时处理的需求

随机推荐

  • Django——工程搭建

    Django的工程搭建 目录 Django的工程搭建一 环境搭建1 创建虚拟环境2 安装Django3 虚拟环境和pip的命令 二 创建工程1 创建2 工程目录说明3 运行开发服务器 三 创建子应用1 创建2 子应用目录说明3 注册安装子应
  • Django——配置静态文件与路由

    静态文件与路由 一 配置文件1 BASE DIR2 DEBUG3 本地语言与时区 二 静态文件三 路由说明1 路由定义位置2 路由解析顺序3 路由命名与reverse反解析 xff08 逆向 xff09 3 1 路由命名3 2 revers
  • Django——请求与响应

    请求与响应 一 请求Request1 URL路径参数2 Django中的QueryDict对象3 查询字符串Query String4 请求体4 1 表单类型 Form Data4 2 非表单类型 Non Form Data 5 请求头6
  • 设计模式——单例模式/工厂模式

    设计模式 前言一 单例模式第 1 种 xff0c 重写 new 方法第 2 种 xff0c 闭包定义装饰器第 3 种 xff0c 多线程中构建单例 二 工厂模式1 简单工厂2 工厂方法3 抽象工厂 前言 常见的设计模式 xff0c 它们是
  • STM32 Futaba SBUS协议解析

    STM32 Futaba SBUS协议解析 1 S BUS1 1 协议介绍1 2 协议解析 2 硬件设计2 1 硬件参数2 2 反相电路 3 程序设计3 1 数据接收3 2 数据处理 4 最后 1 S BUS 1 1 协议介绍 S BUS是
  • Django——类视图

    类视图 前言1 类视图1 类视图引入2 类视图使用 2类视图原理3类视图使用装饰器3 1 在URL配置中装饰3 2 在类视图中装饰 4 类视图Mixin扩展类 前言 提示 xff1a 这里可以添加本文要记录的大概内容 xff1a 例如 xf
  • 设计模式——观察者模式

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 观察者模式 认识观察者模式观察者模式定义一 应用案例二 Python 实现总结 认识观察者模式 我们先看看报纸和杂志的订阅是怎么回事 xf
  • 面向对象设计原则

    面向对象设计原则是设计模式的基础 xff0c 每一个设计模式都符合某一个或者多个面向对象设计原则 xff0c 面向对象设计原则是用于评价一个设计模式的使用效果的重要指标之一 单一职责原则 一个对象应该只包含单一的职责 xff0c 并且该职责
  • 线程互斥锁 Lock

    线程互斥锁 Lock 前言Python 线程互斥锁1 创建互斥锁锁定资源 解锁资源 Python 线程死锁1 引入库 重点总结 前言 Python 线程互斥锁 1 创建互斥锁 span class token comment 导入线程thr
  • 多线程互斥锁Lock和RLock区别

    Python多线程互斥锁Lock和RLock区别 互斥锁Lock和RLock介绍互斥锁Lock互斥锁RLock区别 特殊情况 互斥锁Lock和RLock介绍 互斥锁Lock Lock被称为 原始锁 xff0c 原始锁是一个 在锁定时不属于特
  • 信号量semaphore

    python线程信号量semaphore 前言一 简介semaphore信号量相关函数semaphore信号量使用 前言 一 简介 多线程同时运行 xff0c 能提高程序的运行效率 xff0c 但是并非线程越多越好 xff0c 而semap
  • 线程事件 Event

    Python 线程事件 Event 前言一 介绍二 原理三 应用四 总结 前言 一 介绍 set 全局内置标志 Flag xff0c 将标志 Flag 设置为 True 通知在等待状态 wait 的线程恢复运行 isSet 获取标志 Fla
  • 线程定时器 Timer

    Python 线程定时器 Timer 原理 前言一 原理二 应用总结 前言 一 原理 指定时间间隔后启动线程 xff01 适用场景 xff1a 完成定时任务 xff0c 例如 xff1a 定时提醒 闹钟等等 span class token
  • 线程条件变量 Condition

    Python 线程条件变量 Condition 函数 前言 一 原理 二 应用 案例一 成语接龙 案例二 生产者与消费者模式 总结 前言 Python 互斥锁 Lock 主要作用是并行访问共享资源时 保护共享资源 防止出现脏数据 Pytho
  • Django——中间件

    Django中间件 前言1 中间件的定义方法2 执行流程3 多个中间件的执行顺序 前言 Django中的中间件是一个轻量级 底层的插件系统 xff0c 可以介入Django的请求和响应处理过程 xff0c 修改Django的输入或输出 中间
  • C语言中关于全局变量的深入理解及使用方式

    以前觉得全局变量很简单 xff0c 没有好好思考和理解 xff0c 看到其他项目中的一些用法不太理解 xff0c 最近正好有空 xff0c 好好梳理了一下关于全局变量的定义和引用 总的来说 xff0c 有以下几种方式 xff1a 方式1 x
  • Django——jinja2模板

    Django中使用jinja2模板 前言jinja2介绍1 安装jinja2模块2 Django配置jinja23 jinja2模板的使用绝大多数和Django自带模板一样4 jinja2自定义过滤器 前言 jinja2介绍 Jinja2
  • Django——自带模板

    Django自带模板使用 前言1 模板配置使用1 配置2 定义模板3 模板渲染 2 模板语法2 1 模板变量2 2 模板语句 3 过滤器4 模板继承5 模板注释 前言 1 模板配置使用 1 配置 在工程中创建模板目录templates 在s
  • Django——CSRF

    CSRF 前言CSRF防止 CSRF 攻击CSRF TOKEN的设置过程 前言 CSRF CSRF全拼为Cross Site Request Forgery xff0c 译为跨站请求伪造 CSRF指攻击者盗用了你的身份 xff0c 以你的名
  • 设计模式——工厂模式

    工厂方法模式 1 前言2 简介3 概念示例4 应用示例5 适用场景6 实现方式7 工厂方法模式优缺点8 与其它模式的关系 1 前言 工厂模式属于创建型模式 xff0c 它提供了一种创建对象的最佳方式 在工厂模式中 xff0c 我们在创建对象