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.rhkb.cn/news/450988.html

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

相关文章

OPENSSL-2023/11/10学习记录-C/C++对称分组加密DES

对称分组加密常用算法&#xff1a; DES 3DES AES 国密SM4 对称分组加密应用场景&#xff1a; 文件或者视频加密 加密比特币私钥 消息或者配置项加密 SSL通信加密 对称分组加密 使用异或实现一个简易的对称加密算法 A明文 B秘钥 AB密文AB (AB)B A 密码补全和初始化 数…

第六节——从深层剖析qsort的使用(让你不再害怕指针)

文章目录 1.什么是回调函数2.qsort的使用qsort排序整形数据qsort排序结构体数据qsort排序字符串数据 3.qsort的模拟实现 1.什么是回调函数 回调函数就是⼀个通过函数指针调用的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另⼀个函数&#xff0c;当…

Python画笔案例-087 绘制 旋转的文字

1、绘制 旋转的文字 通过 python 的turtle 库绘制 旋转的文字,如下图: 2、实现代码 绘制 旋转的文字,以下为实现代码: """旋转的文字.py """ import time from turtle import * from write_patch import *screen = Screen

【JPCS独立出版 | 福州大学主办 | 有确定的ISSN号】第三届可再生能源与电气科技国际学术会议(ICREET 2024)

第三届可再生能源与电气科技国际学术会议&#xff08;ICREET 2024&#xff09; 2024 3rd International Conference on Renewable Energy and Electrical Technology ICREET 2024已成功申请JPCS - Journal of Physics: Conference Series (ISSN:1742-6596) 独立出版&#xf…

架构设计笔记-16-嵌入式系统架构设计理论与实践

目录 知识要点 嵌入式微处理器 存储器&#xff08;memory&#xff09; 内&#xff08;外&#xff09;总线逻辑 嵌入式操作系统&#xff08;Embedded Operating System&#xff0c;EOS&#xff09; 通用中间件 嵌入式中间件的一般架构 典型嵌入式中间件系统 案例分析 1…

搭建mongodb单机部署-认证使用

搭建mongodb单机部署-认证使用 实现思路 先将配置文件配置好&#xff0c;使用不用认证的启动命令启动docker&#xff0c;然后创建账号并制定角色。在使用开启认证的命令重新启动容器就好。 这里我并没有说先停止容器&#xff0c;删掉容器重新创建容器。是因为我的启动命令中…

机器学习—Motivations

学习了线性回归&#xff0c;它预测了一个数字&#xff0c;接下来学习分类&#xff0c;输入变量y只能接收少数几个可能的值中的一个&#xff0c;而不是无限范围内的任何数字。事实证明&#xff0c;线性回归不是分类问题的好算法。这将引入一种不同的算法&#xff0c;叫做Logisti…

立仪科技:光谱共焦传感器精准测量玻璃

光谱共焦测量技术作为一种创新的光学检测方法&#xff0c;近年来在工业领域引起了广泛关注。 它以其高精度、非接触式的特点&#xff0c;特别适用于透明或半透明材料如玻璃的厚度和表面形貌测量。 接下来&#xff0c;立仪科技小编将深入探讨光谱共焦技术在玻璃测量上的应用及其…

【MySQL】增删改查-进阶(一)

目录 &#x1f334;数据库约束 &#x1f6a9;约束类型 &#x1f6a9;NOT NULL &#x1f6a9;UNIQUE &#x1f6a9;DEFAULT &#x1f6a9;PRIMARY KEY &#x1f6a9;FOREIGN KEY &#x1f6a9;CHECK &#x1f384;表的设计 &#x1f6a9;一对一 &#x1f6a9;一对多 …

Spring Boot知识管理:智能搜索与分析

3系统分析 3.1可行性分析 通过对本知识管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本知识管理系统采用JAVA作为开发语言&#xff0c;Spring Boot框…

如何做好SQL 数据库安全

随着信息技术的迅猛发展&#xff0c;数据库在现代信息系统中的重要性日益凸显。无论是电子商务平台、金融系统还是社交媒体应用&#xff0c;数据库都是其核心组件之一。其中&#xff0c;SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;数据库…

微信小程序使用MQTT连接阿里云

目录 一、新建项目和项目整体配置​ 二、MQTT 下载引入和配置连接​ 三、阿里云配置 1、创建产品及设备 2、数据进行云流转 四、创建 MQTT 连接​ 五、微信小程序配置 六、效果展示 1、微信小程序发送控制命令 2、LED台灯反馈LED状态 七、微信小程序项目完整代码 一…

论文笔记:PTR: Prompt Tuning with Rules for Text Classification

Abstract 手动设计大量语言提示麻烦且易出错&#xff0c;而自动生成的提示&#xff0c;在非小样本场景下验证其有效性昂贵且耗时。因此&#xff0c;提示调优以处理多类别分类任务仍然具有挑战。为此&#xff0c;本文提出使用规则进行多类别文本分类提示调优&#xff08;PTR&…

Linux发展与基础

Linux基础知识 Shell 命令执行环境&#xff1a; 命令提示符的组成&#xff1a;(用户名主机名)-[当前路径]权限提示符,例&#xff1a;&#xff08;kali㉿kali)-[~]$ ~ 表示所在目录为家目录:其中root用户的家目录是/root&#xff0c;普通用户的家目录在/home下 # 表示用户的权…

C#学习笔记(二)

C#学习笔记&#xff08;二&#xff09; 第 二 章 命名空间和类、数据类型、变量和代码规范一、命名空间-namespace1. 作用与具体表达形式-using2. 命名空间如何分类&#xff1f;3. 命名空间的命名规范 第 二 章 命名空间和类、数据类型、变量和代码规范 深水区 一、命名空间-…

掌握高效工作汇报技巧:如何利用即时白板打造完美日报,提升职场影响力

在快节奏的工作环境中&#xff0c;撰写工作日报、周报和月报已成为职场人士的日常任务。一份精心准备的工作汇报不仅能够体现我们的敬业精神&#xff0c;还能吸引上级的注意&#xff0c;提升我们在团队中的能见度。使用即时白板作为辅助工具&#xff0c;可以让我们更高效地梳理…

分析调优、性能测试曲线图

目录 一、分析调优 性能测试分析的关键指标 分析步骤 收集数据&#xff1a; 找到瓶颈&#xff1a; 性能调优策略 调优硬件资源&#xff1a; 数据库调优&#xff1a; 持续监控和改进 二、性能测试曲线图 1. 轻负载阶段&#xff08;Light Load&#xff09; 2. 重负载…

mqtt客户端订阅一直重复连接?

文章 前言错误场景问题分析解决方案后言 前言 ✨✨ 他们是天生勇敢的开发者&#xff0c;我们创造bug&#xff0c;传播bug&#xff0c;毫不留情地消灭bug&#xff0c;在这个过程中我们创造了很多bug以供娱乐。 前端bug这里是博主总结的一些前端的bug以及解决方案&#xff0c;感兴…

D37【python 接口自动化学习】- python基础之函数

day37 函数的参数&#xff08;上&#xff09; 学习日期&#xff1a;20241014 学习目标&#xff1a;函数&#xfe63;-50 函数的参数&#xff1a;怎样实现函数与外部数据通信&#xff1f; 学习笔记&#xff1a; 实参与形参 代码实现 # 实参与形参 def foo(number):print(nu…

热更新解决方案2 —— Lua语法相关知识点

概述 开发环境搭建 Lua语法 1.第一个Lua程序 2.变量 print("******变量*******"); --lua当中的简单变量类型 -- nil number string boolean -- lua 中所有的变量声明 都不需要声明变量类型 它会自动的判断类型 -- 类似C# 中的var --lua中的一个变量 可以随便赋值 ——…