php常用设计模式之工厂模式

引言

在日常开发中,我们一些业务场景需要用到发送短信通知。然而实际情况考虑到不同厂商之间的价格、实效性、可能会出现的情况等 我们的业务场景往往会接入多个短信厂商来保证我们业务的正常运行,而不同的短信厂商(如阿里云短信、腾讯云短信等)提供了不同的接口,如果我们要支持多个短信厂商,代码中就会充斥着各种 new 操作来创建不同的短信服务实例。

假设在不使用设计模式的情况下,我们的代码可能会这样写:

<?php
// 如果要发送阿里云短信
$aliSms = new AliSms();
$aliSms->send($phoneNumber, $message);// 如果要发送腾讯云短信
$tenSms = new TencentSms();
$tenSms->send($phoneNumber, $message);

这样做的问题是显而易见的:

  1. 代码耦合度高
    每当需要引入新的短信厂商,代码中都必须手动创建该厂商的实例,导致代码与具体厂商类的耦合度非常高,不易维护。
  2. 扩展性差
    如果要增加或替换短信厂商,就需要在每个发送短信的地方都修改代码。这不仅增加了出错的风险,还降低了代码的可扩展性。
  3. 不符合开闭原则
    代码应该是对扩展开放、对修改封闭的。而上述代码违背了这一原则,因为每次增加新厂商时都需要修改已有代码。

为了解决这些问题,我们可以引入工厂模式,通过一个工厂类来负责短信服务的创建,从而实现代码的解耦和易扩展。

工厂模式

工厂模式(Factory Pattern)是一种创建型设计模式,它通过定义一个接口或抽象类,将对象的创建过程封装在工厂类中,而不是直接在代码中实例化对象。工厂模式的核心思想是通过工厂类来决定实例化哪一个具体类,从而使客户端代码不需要关心对象的创建逻辑。

工厂模式的原理

工厂模式的原理是将对象的创建过程封装在一个独立的工厂类中。工厂类通常根据传入的参数或配置信息,决定返回哪个具体的类实例。这样一来,客户端代码只需要调用工厂类提供的创建方法,而不需要直接调用构造函数,从而实现对象的创建与客户端的分离。


工厂模式的实现一般包含以下几部分:

  1. 产品接口或抽象类:定义所需的产品类型,所有的具体产品类都要实现该接口或继承该抽象类。
  2. 具体产品类:实现或继承产品接口或抽象类,代表具体的产品。
  3. 工厂类:封装创建对象的逻辑,根据客户端的需求,生成并返回对应的产品实例。

工厂模式的示意图

工厂模式的结构通常如下:

  • 客户端 ——> 工厂类 ——> 产品接口/抽象类 ——> 具体产品类

这种结构将对象的创建过程从客户端代码中移除,交由工厂类处理,从而实现了创建与使用的解耦。


工厂模式的适用场景

工厂模式适用于以下几种场景:

  1. 需要生成多个具有相似特征的对象
    如果一个系统中需要创建多个相似的对象,且这些对象具有相同的接口或抽象类,工厂模式可以将对象的创建集中管理,方便后期的扩展。
  2. 系统的扩展性要求较高
    如果一个系统可能会频繁地扩展新功能,需要新增类型的对象,工厂模式能使新产品的引入变得简单。新增产品时只需要增加一个具体产品类和相应的工厂逻辑,而不需要修改原有的客户端代码。
  3. 隐藏对象的创建逻辑
    如果对象的创建过程比较复杂,不希望让客户端了解创建细节,通过工厂模式可以将创建逻辑封装在工厂类中,让客户端只关心如何使用对象,而不关心如何创建对象。

工厂模式的实际业务应用场景

  1. 多渠道通知发送
    在消息通知系统中,可能需要支持多种通知渠道,比如短信、邮件、微信、Push 推送等。工厂模式可以用于创建不同的通知服务实例,帮助根据不同渠道自动生成相应的通知对象,使得系统可以方便地切换或扩展新渠道。
  2. 支付渠道对接
    支付系统中通常需要对接多个支付渠道,如支付宝、微信支付、PayPal 等。工厂模式可以根据用户的选择或系统配置,创建相应的支付实例,从而使系统更加灵活,方便地接入新支付渠道。
  3. 数据库连接管理
    在复杂应用中,可能需要使用多种数据库(例如 MySQL、MongoDB、Redis 等)来处理不同的数据存储需求。工厂模式可以根据需求创建不同的数据库连接实例,使代码解耦,并方便地切换或扩展数据库支持。
  4. 文件解析
    系统中需要解析多种文件格式(如 JSON、XML、CSV 等)时,可以使用工厂模式创建对应的解析器对象。例如,创建 JSON 解析器或 CSV 解析器,按需解析不同格式的文件。
  5. 日志系统
    日志系统中可能会使用多种输出方式,比如文件、数据库、远程服务器等。工厂模式可以根据配置创建不同的日志对象,以支持多种日志写入方式,并能灵活地调整日志输出策略。
  6. 用户权限管理
    对于复杂权限管理系统,不同角色可能需要不同的权限实例。工厂模式可以用于根据用户角色创建对应的权限管理对象,从而更好地管理和维护权限规则。
  7. 多语言支持
    在多语言应用中,不同语言的翻译方式可能不同。工厂模式可以根据语言代码生成对应的翻译服务实例,以便于按需加载不同的翻译逻辑。
  8. 图表生成
    在数据可视化系统中,可能会支持多种图表类型(如柱状图、折线图、饼图等)。工厂模式可以用于根据用户选择生成不同的图表对象,以便动态生成所需的图表类型。

在代码中使用工厂模式的好处

使用工厂模式带来了多个显著的好处,使得代码更加灵活、易维护和可扩展:

  1. 解耦创建和使用
    工厂模式将对象的创建和使用分离,使客户端代码无需关心对象的具体实现类,也不需要直接创建实例。这种解耦使得代码更灵活,便于在不同的情况下切换不同的实现。
  2. 提高代码的可维护性
    工厂模式将对象的创建逻辑集中在工厂类中,客户端代码只与工厂类交互。当需要添加新的对象类型或修改对象的创建逻辑时,只需更改工厂类,而无需修改客户端代码,大大减少了出错的风险。
  3. 符合开闭原则
    工厂模式使代码更符合开闭原则(对扩展开放,对修改封闭)。如果需要添加新的产品类,只需在工厂类中增加对应的创建逻辑,而无需修改现有的客户端代码或其他产品类代码,减少了代码的修改需求。
  4. 增强代码的可扩展性
    当需要增加新的产品类型时,只需创建一个新的产品类,并在工厂类中定义相应的创建逻辑。工厂模式使得扩展新产品变得简单,而不需要更改已有代码结构。
  5. 简化客户端代码
    工厂模式将对象创建逻辑封装起来,简化了客户端代码,使其专注于如何使用对象,而不是关心如何创建对象。这使得代码更加清晰和易于理解。

通过这些好处,工厂模式让代码具备了更好的结构和维护性,尤其适用于需要频繁创建和扩展对象的场景。


工厂模式的类型

在工厂模式中,根据需求的不同,常见的有三种主要变体:简单工厂模式工厂方法模式抽象工厂模式。这些模式的主要目标都是将对象的创建与使用解耦,但在设计和使用场景上各有不同。

1. 简单工厂模式

简单工厂模式(Simple Factory Pattern)是工厂模式的基础实现,通过一个工厂类的静态方法,根据传入的参数来创建并返回不同的产品对象。这种模式使用简单,便于理解。

  • 特点:通过一个静态方法来创建对象,工厂类负责所有产品实例的创建。
  • 适用场景:适用于产品种类较少、对象创建逻辑简单的场景。
  • 缺点:违反开闭原则,当新增产品时必须修改工厂类,系统扩展性较低。
示例代码
class SmsFactory {public static function create($type) {switch ($type) {case 'Ali':return new AliSms();case 'Tencent':return new TencentSms();default:throw new Exception("未知短信类型");}}
}

2. 工厂方法模式

工厂方法模式(Factory Method Pattern)为每个产品提供一个具体的工厂类,通过实现一个工厂接口,负责创建各自的产品。这种方式将工厂类分离,符合开闭原则。

  • 特点:每个产品类都有一个对应的工厂类,通过实现工厂接口来创建具体产品。
  • 适用场景:适合产品种类多、频繁扩展新产品的情况,便于维护。
  • 缺点:增加了系统的复杂度,特别是当产品种类较多时,会有大量工厂类。
示例代码
interface SmsFactory {public function create();
}class AliSmsFactory implements SmsFactory {public function create() {return new AliSms();}
}class TencentSmsFactory implements SmsFactory {public function create() {return new TencentSms();}
}

3. 抽象工厂模式

抽象工厂模式(Abstract Factory Pattern)是一种更为复杂的工厂模式,用于创建多个相关的产品对象(即产品族)。每个具体工厂负责创建一组相关的对象,从而保证产品族的一致性。

  • 特点:可以创建一组相关或相互依赖的对象(产品族),每个具体工厂实现多个工厂接口,负责创建整个产品族。
  • 适用场景:当系统需要多个相互关联的对象组合时(例如支持多平台的 UI 组件库)。
  • 缺点:扩展新产品族较为复杂,需要修改或增加多个类。
示例代码
interface SmsAbstractFactory {public function createSms();public function createTemplate();
}class AliSmsFactory implements SmsAbstractFactory {public function createSms() {return new AliSms();}public function createTemplate() {return new AliTemplate();}
}class TencentSmsFactory implements SmsAbstractFactory {public function createSms() {return new TencentSms();}public function createTemplate() {return new TencentTemplate();}
}

工厂模式类型的区别与适用场景总结

工厂模式类型特点适用场景缺点
简单工厂模式单一工厂类,通过静态方法创建对象产品种类少、扩展性需求低不符合开闭原则,扩展性差
工厂方法模式每个产品对应一个工厂类,符合开闭原则产品种类多,需支持系统扩展性增加系统复杂度,工厂类较多
抽象工厂模式可以创建多个相关对象(产品族),满足复杂对象创建需求需要创建一组相关对象组合(产品族)扩展复杂,类结构较复杂

通过这种对比表格和示例代码,读者可以更清晰地了解不同工厂模式的特点与适用场景,并能根据项目需求选择合适的工厂模式类型。


回到开头 为了更好地管理多种短信厂商的集成,我们可以选择使用工厂模式来设计短信发送逻辑。这样一来,我们可以轻松地根据需求切换或添加新的短信厂商,而无需修改客户端代码。

设计步骤
  1. 定义一个短信接口:创建一个 SmsServiceInterface,定义发送短信的基本方法,比如 send($phoneNumber, $message)。所有的具体短信服务(如阿里云、腾讯云)都将实现这个接口。

    interface SmsServiceInterface {public function send($phoneNumber, $message);
    }
    
  2. 创建具体的短信服务类:为每个短信厂商创建具体实现类,比如 AliSmsServiceTencentSmsService。每个类都实现 SmsServiceInterface 接口,包含具体的发送逻辑。

    class AliSmsService implements SmsServiceInterface {public function send($phoneNumber, $message) {// 实现阿里云短信的发送逻辑}
    }class TencentSmsService implements SmsServiceInterface {public function send($phoneNumber, $message) {// 实现腾讯云短信的发送逻辑}
    }
    
  3. 创建短信工厂类:创建 SmsFactory 类,根据传入的厂商类型来决定返回的短信服务实例。这样一来,客户端代码就不需要直接实例化具体的短信类,而是通过工厂类来获取相应的实例。

    class SmsFactory {public static function create($type) {switch ($type) {case 'Ali':return new AliSmsService();case 'Tencent':return new TencentSmsService();default:throw new Exception("未知短信类型");}}
    }
    
  4. 使用工厂类发送短信:客户端代码只需调用工厂类的 create() 方法来获取对应的短信实例,然后调用 send() 方法即可。这样,客户端代码无需关心具体的实现,只需使用统一的接口。

    $smsService = SmsFactory::create('Ali');
    $smsService->send($phoneNumber, $message);
    
这样设计的好处
  1. 降低耦合性
    客户端代码与具体的短信实现解耦,通过 SmsFactory 工厂类统一管理不同厂商的实例创建,客户端只需知道接口而不需要知道具体实现。
  2. 提高扩展性
    使用工厂模式后,如果要接入新的短信厂商(如华为云),只需增加一个新的实现类(如 HuaweiSmsService),并在工厂类中添加对应的实例创建逻辑,而无需修改客户端代码。
  3. 符合开闭原则
    工厂模式允许我们在不修改客户端代码的情况下扩展新功能,增强了代码的灵活性和稳定性。通过简单扩展工厂类,我们便可以支持新的厂商或新的短信逻辑。
  4. 简化维护
    所有短信实例的创建都集中在 SmsFactory 中,便于管理。若有修改需求,例如调整具体厂商的实现逻辑,我们只需修改对应的实现类,而不必在各处重复更改。

通过这种设计,工厂模式让短信发送逻辑更加灵活、可扩展且便于维护,为系统增加新的厂商支持变得轻松简单。这不仅提升了代码质量,也提高了系统的稳定性。


最后

通过工厂模式,我们有效地将对象的创建过程与使用逻辑解耦,使系统的扩展性和维护性得到了显著提升。在多厂商短信发送的场景中,工厂模式使得不同厂商的短信服务可以通过统一的接口进行调用,简化了代码结构。同时,工厂模式还符合开闭原则,让我们能够在不修改已有代码的前提下,轻松地接入新的厂商支持。这样的设计不仅提高了代码的灵活性和可读性,也降低了系统的耦合度,使项目更易于维护和拓展。

工厂模式在实际开发中应用广泛,尤其适用于需要动态生成对象且对象种类较多的场景。通过合理运用工厂模式,我们可以更有效地应对业务需求的变化,让系统保持稳定、可扩展的同时具备更高的质量。

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

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

相关文章

R语言机器学习算法实战系列(八)逻辑回归算法 (logistic regression)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍教程下载数据加载R包导入数据数据预处理数据描述数据切割构建模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve特征的重要性保存模型总结系统信息介绍 …

MySQL数据库和表的基本操作

文章目录 一、数据库的基础知识 背景知识数据库的基本操作二、数据类型 字符串类型数值类型日期类型三、表的基本操作 创建表查看表结构查看所有表删除表 一、数据库的基础知识 背景知识 MySQL是一个客户端服务器结构的程序 主动发送数据的这一方&#xff0c;客户端(client…

“智改数转”转了什么?

万界星空科技专门针对数字化改造申报的MES系统具有显著的技术优势和实施效果&#xff0c;能够为制造型企业提供全方位、高效、可靠的数字化转型支持。项目合作可以私信或者百度上海万界星空科技官网。 “智改数转”是一个综合性的过程&#xff0c;涉及企业多个方面的转型和升…

【python实战】利用代理ip爬取Alibaba海外版数据

引言 在跨境电商的业务场景中&#xff0c;数据采集是分析市场、了解竞争对手以及优化经营策略的重要环节。然而&#xff0c;随着越来越多企业依赖数据驱动决策&#xff0c;许多跨境电商平台为了保护自身数据&#xff0c;采取了更严格的防护措施。这些平台通过屏蔽大陆IP地址或部…

【Spring声明式事务失效的12种场景测试】

文章目录 一.Spring声明式事务是什么&#xff1f;二.Spring事务失效的12种场景1.访问权限问题 小结 一.Spring声明式事务是什么&#xff1f; Spring声明式事务是一种通过配置的方式管理事务的方法&#xff0c;它通过注解或XML配置来声明哪些方法需要事务管理&#xff0c;从而将…

JRT怎么从IRIS切换到PostGreSql库

1.执行M导出得到建库脚本文件 2.下载生成的脚本到本地D盘 3.修改驱动为PostGreSql 4.修改连接串 5.到PostGreSql里面创建一个jrtlis的数据库&#xff0c;模式为jrt 6.启动网站点击导入脚本按钮 导入完成了就可以正常使用PostGreSql库了

OpenCV高级图形用户界面(14)交互式地选择一个或多个感兴趣区域函数selectROIs()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 允许用户在给定的图像上选择多个 ROI。 该函数创建一个窗口&#xff0c;并允许用户使用鼠标来选择多个 ROI。控制方式&#xff1a;使用空格键或…

Google FabricDiffusion:开启3D虚拟试穿新篇章

随着数字化转型的步伐不断加快,时尚界也在探索如何利用最新技术为消费者带来更加沉浸式的购物体验。在这一背景下,Google 推出了一项名为 FabricDiffusion 的新技术,这项技术能够将2D服装图像中的高质量织物纹理转移到任意形状的3D服装模型上,从而为3D虚拟试穿提供了更为真…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于AGCN-LSTM模型的海上风电场功率概率预测 》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

端到端自动驾驶模型SparseDrive部署过程

SparseDrive 论文链接 https://arxiv.org/pdf/2405.19620 仓库链接 https://github.com/swc-17/SparseDrive 论文和模型的相关介绍大家可以参考其他博客的介绍&#xff0c;这里只介绍模型部署的过程和中间可能遇到的问题解决办法&#xff0c;以及代码解析和使用记录。 模型部署…

CyberRT通信介绍与基于Reader、Writer的通信实践(apollo9.0)

目录 数据通信场景 CyberRT中的通信方式 ​编辑 通信模式 话题通信 服务通信 参数通信 protobuf protobuf简介 protobuf文件编写 topic通信实验 实验环境 实验准备 代码编写 定义消息格式 发送消息 接收消息 定义编译规则 程序编译 运行程序 数据通信场景 …

fabric-sdk-go

Fabric-SDK-go 区块链网络搭建fabric-sdk代码代码结构&#xff1a;代码eg&#xff1a; 区块链网络搭建 使用fabric-sample的网络结构用容器搭建起测试网络即可。 fabric-sdk代码 代码很简易&#xff0c;主要为了了解怎么使用fabric为编程人员提供的sdk从而提供HTTP接口的情况…

浅谈华为 HarmonyOS Next

1. 万物互联时代的新机遇 随着万物互联时代的到来&#xff0c;智能应用从几十亿部手机扩展到数百亿个IoT设备&#xff0c;深刻改变了人们的生活方式。这为我们应用开发者带来了新的机遇和挑战。 机遇 : 目前正处于万物互联时代的前夕&#xff0c;正在经历手机单设备到全场景多…

技术分享:A-23OH型树脂在汽车涂装废溶剂回收中的应用

在当今汽车制造业竞争激烈的环境下&#xff0c;提高生产效率、降低成本的同时&#xff0c;满足环保要求已成为各制造商追求的核心目标。水性涂料因其环保、节能等多重优势&#xff0c;在汽车涂装领域的应用日益广泛。然而&#xff0c;随之而来的喷涂废溶剂处理问题也日益凸显。…

从 Hadoop 迁移到数据 Lakehouse 的架构师指南

从 Hadoop 到数据湖仓一体架构的演变代表了数据基础架构的重大飞跃。虽然 Hadoop 曾经以其强大的批处理能力统治着大数据领域&#xff0c;但如今的组织正在寻求更敏捷、更具成本效益和现代化的解决方案。尤其是当他们越来越多地开始实施 AI 计划时。根本没有办法让 Hadoop 为 A…

三周精通FastAPI:1 第一步入门

FastAPI是一个非常棒的python web和api框架&#xff0c;准备用三周的时间“精通它” 学习流程参考FastAPI官网的用户教程&#xff1a;教程 - 用户指南 - FastAPI 学前提示 运行代码 所有代码片段都可以复制后直接使用&#xff08;它们实际上是经过测试的 Python 文件&#x…

UG NX12.0建模入门笔记:1.0 UG NX12.0安装教程

一、如何关闭防火墙&#xff1f; 提示&#xff1a;安装软件之前&#xff0c;建议先 关闭防火墙和杀毒软件&#xff01;&#xff01;&#xff01; 文章目录 一、如何关闭防火墙&#xff1f;二、UG NX12.0安装包三、UG NX12.0安装教程1.新建文件夹2.安装JAVA环境3.安装许可证管理…

LeetCode_2235. 两整数相加_java

1、问题 2235. 两整数相加https://leetcode.cn/problems/add-two-integers/ 给你两个整数 num1 和 num2&#xff0c;返回这两个整数的和。 示例 1&#xff1a; 输入&#xff1a;num1 12, num2 5 输出&#xff1a;17 解释&#xff1a;num1 是 12&#xff0c;num2 是 5 &am…

智慧监管:EasyCVR视频汇聚智能分析平台助力有限空间作业实现全天候可视化监控

随着工业化和城市化进程的加快&#xff0c;有限空间作业&#xff08;如地下管道、储罐、隧道等&#xff09;在各类企事业单位中日益频繁。然而&#xff0c;这类作业环境复杂、危险系数高&#xff0c;对作业人员的安全管理和实时监控提出了严峻挑战。为了保障有限空间作业的安全…

021_Thermal_Transient_in_Matlab统一偏微分框架之热传导问题

Matlab求解有限元专题系列 固体热传导方程 固体热传导的方程为&#xff1a; ρ C p ( ∂ T ∂ t u t r a n s ⋅ ∇ T ) ∇ ⋅ ( q q r ) − α T d S d t Q \rho C_p \left( \frac{\partial T}{\partial t} \mathbf{u}_{\mathtt{trans}} \cdot \nabla T \right) \nab…