灵活多变的对象创建——工厂方法模式(Python实现)

1. 引言

大家好,又见面了!在上一篇文章中,我们聊了聊简单工厂模式,今天,我们要进一步探讨一种更加灵活的工厂设计模式——工厂方法模式。如果说简单工厂模式是“万能钥匙”,那工厂方法模式就是“变形金刚”。它通过定义一个创建对象的接口,让子类决定实例化哪一个类,从而应对各种变化。今天,我们就来揭开工厂方法模式的神秘面纱,让你的Python代码更加灵活多变。准备好了吗?Let’s go!

2. 什么是工厂方法模式

工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式使一个类的实例化延迟到其子类,简而言之,就是父类提供一个接口,子类来决定实例化哪个具体的类。

3. 工厂方法模式的实现(Python)

示例一:形状工厂

假如你是个艺术家,需要画各种形状,圆形、方形啥的,你可以用工厂方法模式让子类决定创建哪些形状对象:

代码实现
from abc import ABC, abstractmethod# 定义Shape接口
class Shape(ABC):@abstractmethoddef draw(self):pass# 实现Circle类
class Circle(Shape):def draw(self):print("Drawing a Circle")# 实现Square类
class Square(Shape):def draw(self):print("Drawing a Square")# 定义ShapeFactory接口
class ShapeFactory(ABC):@abstractmethoddef create_shape(self):pass# 实现CircleFactory类
class CircleFactory(ShapeFactory):def create_shape(self):return Circle()# 实现SquareFactory类
class SquareFactory(ShapeFactory):def create_shape(self):return Square()# 使用示例
if __name__ == "__main__":circle_factory = CircleFactory()shape1 = circle_factory.create_shape()shape1.draw()  # 输出: Drawing a Circlesquare_factory = SquareFactory()shape2 = square_factory.create_shape()shape2.draw()  # 输出: Drawing a Square
详细代码解析
  • Shape是一个抽象基类,定义了draw抽象方法;
  • CircleSquare类实现了Shape接口,具体画啥样子它们说了算;
  • ShapeFactory是一个抽象基类,定义了create_shape抽象方法;
  • CircleFactorySquareFactory类实现了ShapeFactory接口,分别负责创建CircleSquare对象;
  • 我们只需通过调用具体的工厂类(如CircleFactorySquareFactory)来创建形状对象,然后调用相应的draw方法。
示例二:日志记录器工厂

现在你是个开发者,搞个日志系统,你想要不同级别的日志记录器来帮你分门别类记录信息,工厂方法模式也能派上用场:

码实现
from abc import ABC, abstractmethod# 定义Logger接口
class Logger(ABC):@abstractmethoddef log(self, message):pass# 实现InfoLogger类
class InfoLogger(Logger):def log(self, message):print(f"INFO: {message}")# 实现ErrorLogger类
class ErrorLogger(Logger):def log(self, message):print(f"ERROR: {message}")# 定义LoggerFactory接口
class LoggerFactory(ABC):@abstractmethoddef create_logger(self):pass# 实现InfoLoggerFactory类
class InfoLoggerFactory(LoggerFactory):def create_logger(self):return InfoLogger()# 实现ErrorLoggerFactory类
class ErrorLoggerFactory(LoggerFactory):def create_logger(self):return ErrorLogger()# 使用示例
if __name__ == "__main__":info_logger_factory = InfoLoggerFactory()info_logger = info_logger_factory.create_logger()info_logger.log("This is an informational message.")  # 输出: INFO: This is an informational message.error_logger_factory = ErrorLoggerFactory()error_logger = error_logger_factory.create_logger()error_logger.log("This is an error message.")  # 输出: ERROR: This is an error message.
详细代码解析
  • Logger是一个抽象基类,定义了log抽象方法;
  • InfoLoggerErrorLogger类实现了Logger接口,分别负责记录不同级别的日志;
  • LoggerFactory是一个抽象基类,定义了create_logger抽象方法;
  • InfoLoggerFactoryErrorLoggerFactory类实现了LoggerFactory接口,分别负责创建InfoLoggerErrorLogger对象;
  • 你只需通过调用具体的工厂类(如InfoLoggerFactoryErrorLoggerFactory)来创建日志记录器对象,然后调用相应的log方法。
示例三:数据库连接工厂

假如你现在是个DBA,需要管理多个数据库连接,工厂方法模式同样能帮你搞定这个问题:

代码实现
from abc import ABC, abstractmethod# 定义DatabaseConnection接口
class DatabaseConnection(ABC):@abstractmethoddef connect(self):pass# 实现MySQLConnection类
class MySQLConnection(DatabaseConnection):def connect(self):print("Connecting to MySQL database...")# 实现PostgreSQLConnection类
class PostgreSQLConnection(DatabaseConnection):def connect(self):print("Connecting to PostgreSQL database...")# 定义DatabaseConnectionFactory接口
class DatabaseConnectionFactory(ABC):@abstractmethoddef create_connection(self):pass# 实现MySQLConnectionFactory类
class MySQLConnectionFactory(DatabaseConnectionFactory):def create_connection(self):return MySQLConnection()# 实现PostgreSQLConnectionFactory类
class PostgreSQLConnectionFactory(DatabaseConnectionFactory):def create_connection(self):return PostgreSQLConnection()# 使用示例
if __name__ == "__main__":mysql_factory = MySQLConnectionFactory()mysql_connection = mysql_factory.create_connection()mysql_connection.connect()  # 输出: Connecting to MySQL database...postgresql_factory = PostgreSQLConnectionFactory()postgresql_connection = postgresql_factory.create_connection()postgresql_connection.connect()  # 输出: Connecting to PostgreSQL database...
详细代码解析
  • DatabaseConnection是一个抽象基类,定义了connect抽象方法;
  • MySQLConnectionPostgreSQLConnection类实现了DatabaseConnection接口,分别负责不同数据库的连接;
  • DatabaseConnectionFactory是一个抽象基类,定义了create_connection抽象方法;
  • MySQLConnectionFactoryPostgreSQLConnectionFactory类实现了DatabaseConnectionFactory接口,分别负责创建MySQLConnectionPostgreSQLConnection对象;
  • 你只需通过调用具体的工厂类(如MySQLConnectionFactoryPostgreSQLConnectionFactory)来创建数据库连接对象,然后调用相应的connect方法。

4. 工厂方法模式的优缺点

优点
  • 解耦:将对象的创建过程与使用过程分离,降低了代码的耦合度;
  • 灵活性:通过子类来决定具体实例化哪个类,增加了代码的灵活性;
  • 扩展性:增加新的产品类时,只需添加相应的工厂类即可,不需要修改现有代码。
缺点
  • 类的数量增加:每增加一个产品类,都需要增加一个相应的工厂类,导致类的数量增多;
  • 代码复杂度提高:增加了系统的复杂性,理解起来可能会有些困难。

5. 图示

类图
../_images/FactoryMethod.jpg
示意图

img

6. 总结

工厂方法模式是一个非常有用的设计模式,通过定义一个创建对象的接口,让子类来决定实例化哪一个类,增加了代码的灵活性和可扩展性。虽然它会增加类的数量和代码的复杂度,但在大多数情况下,工厂方法模式依然是一个非常实用的解决方案。希望今天的分享能让大家对工厂方法模式有更深入的理解,如果你在项目中也用到了工厂方法模式,欢迎留言分享你的经验和见解!
在这里插入图片描述

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

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

相关文章

Python采集京东标题,店铺,销量,价格,SKU,评论,图片

京东的许多数据是通过 JavaScript 动态加载的,包括销量、价格、评论和评论时间等信息。我们无法仅通过传统的静态网页爬取方法获取到这些数据。需要使用到如 Selenium 或 Pyppeteer 等能够模拟浏览器行为的工具。 另外,京东的评论系统是独立的一个系统&a…

offer题目33:判断是否是二叉搜索树的后序遍历序列

题目描述:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。例如,输入数组{5,7,6,9,11,10,8},则返回true,,因为这个整数是下图二叉搜索树…

c++内存管理(上)

目录 引入 分析 说明 C语言中动态内存管理方式 C内存管理方式 new/delete操作内置类型 new和delete操作自定义类型 引入 我们先来看下面的一段代码和相关问题 int globalVar 1; static int staticGlobalVar 1; void Test() { static int staticVar 1; int localVar 1…

数图助推朝阳佳惠辽宁华联开启数字化导航、精细化管理新纪元!

近期,辽宁省著名零售企业朝阳佳惠与辽宁华联,秉持创新精神,大胆尝试,在品类空间管理方面推出了创新举措。引入了先进的数图可视化陈列管理系统,通过智能化、直观化的方式优化商品布局。此举不仅大幅提高了商品管理的效…

element-ui封装分页组件:实现首页、上一页、下一页、末页、跳转按钮

首页、上一页、下一页、末页、跳转按钮 因为el-pagination只有一个插槽,所以通过两个el-pagination插槽分别加入首页、末页按钮,再拼接这两个el-pagination的方式来实现首页、末页按钮跳转按钮不用加事件,如果el-pagination修改了前往的页数…

【work】AI八股-神经网络相关

Deep-Learning-Interview-Book/docs/深度学习.md at master amusi/Deep-Learning-Interview-Book GitHub 网上相关总结: 小菜鸡写一写基础深度学习的问题(复制大佬的,自己复习用) - 知乎 (zhihu.com) CV面试问题准备持续更新贴 …

聚类分析方法(二)

目录 三、层次聚类方法(一)层次聚类策略(二)AGNES算法(三)DIANA算法 四、密度聚类方法(一)基本概念(二)算法描述(三)计算实例&#xf…

Google账号输入用户名和密码后提醒要到手机通知点是,还要点击数字,但是我手机收不到

有一些朋友换了一个新的电脑后手机登录谷歌账号时,用户名和密码都正确输入以后,第三步弹出一个提示,要在手机上的通知栏点击是,并且点击手机上相应的数字才能继续登录。 但是自己的手机上下拉通知栏却没有来自谷歌的通知&#xf…

符号同步、定时同步和载波同步

符号同步、定时同步和载波同步是通信系统中重要的同步技术,它们各自承担着不同的功能和作用。以下是对这三种同步技术的详细解释: 符号同步 定义: 符号同步,也称为定时恢复或时钟恢复,是指在数字通信系统中&#xff…

继承关系中的访问控制

继承关系中的访问控制 类中成员的访问权限类继承中的访问权限派生类向基类转换的权限问题(向上转型)友元在继承中的访问权限 类中成员的访问权限 public:类的对象(外部)可以访问,派生类也可以访问protecte…

LeNet原理及代码实现

目录 1.原理及介绍 2.代码实现 2.1model.py 2.2model_train.py 2.3model.test.py 1.原理及介绍 2.代码实现 2.1model.py import torch from torch import nn from torchsummary import summaryclass LeNet(nn.Module):def __init__(self):super(LeNet, self).__init__…

nuxt、vue树形图d3.js

直接上代码 //安装 npm i d3 --save<template><div class"d3"><div :id"id" class"d3-content"></div></div> </template> <script> import * as d3 from "d3";export default {props: {d…

Github Actions 构建Vue3 + Vite项目

本篇文章以自己创建的项目为例&#xff0c;用Github Actions构建。 Github地址&#xff1a;https://github.com/ling08140814/myCarousel 访问地址&#xff1a;https://ling08140814.github.io/myCarousel/ 具体步骤&#xff1a; 1、创建一个Vue3的项目&#xff0c;并完成代…

接口基础知识1:认识接口

课程大纲 一、定义 接口&#xff1a;外部与系统之间、内部各子系统之间的交互点。 比如日常使用的电脑&#xff0c;有电源接口、usb接口、耳机接口、显示器接口等&#xff0c;分别可以实现&#xff1a;与外部的充电、文件数据传输、声音输入输出、图像输入输出等功能。 接口的本…

262个地级市-市场潜力指数(do文件+原始文件)

全国262个地级市-市场潜力指数&#xff08;市场潜力计算方法代码数据&#xff09;_市场潜力数据分析资源-CSDN文库 市场潜力指数&#xff1a;洞察未来发展的指南针 市场潜力指数是一个综合性的评估工具&#xff0c;它通过深入分析市场需求、竞争环境、政策支持和技术创新等多个…

(2)滑动窗口算法练习:无重复字符的最长子串

无重复字符的最长子串 题目链接&#xff1a;3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的最长子串的长度。 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是"a…

mov视频怎么改成mp4?把mov改成MP4的四个方法

mov视频怎么改成mp4&#xff1f;选择合适的视频格式对于确保内容质量和流通性至关重要。尽管苹果公司的mov格式因其出色的视频表现备受赞誉&#xff0c;但在某些情况下&#xff0c;它并非最佳选择&#xff0c;因为使用mov格式可能面临一些挑战。MP4格式在各种设备&#xff08;如…

构造二进制字符串

目录 LeetCode3221 生成不含相邻零的二进制字符串 #include <iostream> #include <vector> using namespace std;void dfs(string s,int n,vector<string>& res){if(s.size()n){res.push_back(s);return;}dfs(s"0",n,res);dfs(s"1"…

使用redis进行短信登录验证(验证码打印在控制台)

使用redis进行短信登录验证 一、流程1. 总体流程图2. 流程文字讲解&#xff1a;3.代码3.1 UserServiceImpl&#xff1a;&#xff08;难点&#xff09;3.2 拦截器LoginInterceptor&#xff1a;3.3 拦截器配置类&#xff1a; 4 功能实现&#xff0c;成功存入redis &#xff08;黑…

java中 使用数组实现需求小案例

Date: 2024.04.08 18:32:57 author: lijianzhan 需求实现&#xff1a; 设计一个java类&#xff0c;java方法&#xff0c;根据用户手动输入的绩点&#xff0c;从而获取到绩点最高的成绩。 实现业务逻辑的代码块 import java.util.Scanner;public class PointDemo {/*** 需求&…