php中常见的几种设计模式

2023-05-16

1. 单例模式

单例模式可以说是面向对象语言里最常用、也是最简单的一种模式。单例就是单个实例,单个对象的意思,就是说我们去实例化一个类的时候,不管调用多少次,都永远只有一个实例, 不会有多个,这样就节省了内存分配开支。

先简单说下单例模式的原理:将构造函数__construct设置为私有的private, 对外提供一个static静态方法比如:getInstance获得实例,在getInstance方法里来做统一判断是否有new一个实例,有的话直接返回,没有就new一个,这样就保证了不管外面调用多少次,只保证new了一次

直接上代码,举例来说明下:


  
  1. <?php
  2. /**
  3. * 1.单例模式 Single.php
  4. */
  5. namespace Lib;
  6. class Single {
  7. static private $instance;
  8. /**
  9. * 在单例模式下,要将构造方法设为私有的,这样在外部就不能实例化
  10. */
  11. private function __construct()
  12. {
  13. }
  14. /**
  15. * 这个是获取实例化对象的入口,是静态方法,不需要new,可以直接调用
  16. */
  17. static public function getInstance()
  18. {
  19. //判断一下$instance是否已经存在
  20. if (!isset(self::$instance)) {
  21. self::$instance = new Single();
  22. }
  23. return self::$instance;
  24. }
  25. public function get()
  26. {
  27. echo 'you get it!';
  28. }
  29. }

在index.php中,我们来实例化调用一下。


  
  1. define('WWWDIR', __DIR__);
  2. include WWWDIR . '/Lib/Load.php';
  3. spl_autoload_register('\\Lib\\Load::autoload');
  4. // 1. 单例模式
  5. $single = Lib\Single::getInstance();
  6. $single2 = Lib\Single::getInstance();
  7. $single->get(); //you get it!
  8. $single2->get(); //you get it!
  9. var_dump($single === $single2); //boolean true

从打印结果我们看出,$single1和$single2其实是同一个引用的,这样就达到了我们的目的。而且也节约了内存。在同一个生命周期中,不管调用多次,永远只有一个实例。

2. 工厂模式

先简单说下工厂模式:当我要实例化类的时候,不直接new这个类,而是通过调用另一个类的一个方法来实例化。这就是工厂模式的核心原理。

这样的好处有啥呢?

  1. 假设不使用工厂模式:比如很多地方调用类class_a,代码就会这样子创建一个实例:new class_a(), 假设某天需要把class_a类的名子修改成class_b,意味着很多调用的代码都要修改。如果你用工厂模式,就你只需要改一处就可以了。当然这只是一个很极端的例子,没人会吃饱了没事干会去修改类名。这也是工厂模式最简单的用法。
  2. 工厂模式最多的用法,就是根据条件来创建不同的实例,比如你传入一个mysql,我去实例化mysql类给你,你传入sql server,那我就实例化sql server类给你。有点像switch干的活。这样就简化了逻辑,统一控制,代码也比较简化。

还是直接上代码来说明:


  
class Single {

    protected static $_instance;
    protected $name = '';
    
    声明私有构造方法为了防止外部代码使用new来创建对象。
    private function __construct(){
    }

   //防止克隆
    private function __clone(){
    }

    //声明一个getinstance()静态方法,用于检测是否有实例对象
    public static function getInstance()
    {
        if (self::$_instance === null) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }
        
    public function setname($n){ 
        $this->name = $n; 
    }
        
}

  

  
  1. //2.工厂模式
  2. //不用工厂模式,我在很多地方调用一个类是这样的。
  3. new Lib\FactoryTest1();
  4. new Lib\FactoryTest1();
  5. new Lib\FactoryTest1();
  6. new Lib\FactoryTest1();
  7. //用工厂模式,我不直接去new FactoryTest1这个类,我是通过Factory这个类的create方法来
  8. $test1 = Lib\Factory::create();
  9. $test1->get();
  10. //假如我现在吃饱了犯傻,将FactoryTest1的类名改成FactoryTest2了,普通的写法是这样:
  11. new Lib\FactoryTest2();
  12. new Lib\FactoryTest2();
  13. new Lib\FactoryTest2();
  14. new Lib\FactoryTest2();
  15. //用了工厂模式,还是一模一样的写法,我通过修改Factory类里的create方法这一处就可以了。
  16. $test1 = Lib\Factory::create();
  17. $test1->get();

工厂模式的第二张方式就是统一管理,通过传参的方式来实例化,最常见的DB,也可能有Mysql,也有可能有SqlServer:


  
  1. static public function db($dbName)
  2. {
  3. if (empty($dbName)) {
  4. return new Db\Mysql();
  5. }
  6. //选择判断
  7. switch ($dbName) {
  8. case 'mysql':
  9. return new Db\Mysql();
  10. break;
  11. case 'sql_server':
  12. return new Db\SqlServer();
  13. break;
  14. }
  15. }

这样,我就可以通过参数传递的方式,来选择数据库连接了。


  
  1. //通过传参数的方式来实现切换不同的数据库实例
  2. $mysql = Lib\Factory::db('mysql');
  3. $sqlServer = Lib\Factory::db('sql_server');

3. 注册模式

注册模式单例模式的基础上进一步拓展了一步,他把所有单例模式的对象全部保存在一个索引数组中,下次取得时候,直接去数组里按照索引去取。这样的好处是在程序中有条理的存放并管理对象。所以,肯定有一个存(set)和一个取(get)。一般是先去如果没有,就重新初始化然后起来,这样这个周期中的其他代码就可以直接了。和redis缓存的道理是一样的。

我先来实现注册模式的代码:


  
  1. <?php
  2. /**
  3. * 3. 注册模式
  4. *
  5. * @package Register.php
  6. */
  7. namespace Lib;
  8. class Register
  9. {
  10. protected static $_config;
  11. /**
  12. * 获取变量值
  13. *
  14. * @param string $name
  15. * @return string
  16. */
  17. public static function get($name)
  18. {
  19. if (self::isRegister($name)) {
  20. return self::$_config[$name];
  21. }
  22. else {
  23. return false;
  24. }
  25. }
  26. /**
  27. * 设置变量值
  28. *
  29. * @param string $name
  30. * @param string $value
  31. * @return string
  32. */
  33. public static function set($name, $value)
  34. {
  35. if (!isset($name) || !isset($value)) {
  36. return false;
  37. }
  38. self::$_config[$name] = $value;
  39. return self::$_config[$name];
  40. }
  41. /**
  42. * 判断变量是否注册
  43. *
  44. * @param string $name
  45. * @return bool
  46. */
  47. public static function isRegister($name)
  48. {
  49. return isset(self::$_config[$name]);
  50. }
  51. }

上面代码我们定义了getset方法。这样在一个存取模式就定义好了。

在我们的项目当中,像数据库连接redis连接配置文件等,都是在各个地方被大量使用的,我们完全可以使用注册模式,将这些变量对象写到注册器上面去。

下面我们来调用一下:


  
  1. // 3. 注册模式
  2. $register = Lib\Register::getInstance();
  3. //数据库链接提前写到注册树上
  4. $register->set('mysql', new Lib\Db\Mysql());
  5. $register->set('sql_server', new Lib\Db\SqlServer());
  6. //读取db类
  7. $register->get('mysql')->select('select * from user');
  8. $register->get('sql_server')->select('select * from user');
  9. //redis类写到注册树上
  10. $register->set('redis', new Lib\Cache\Redis());
  11. //读取redis类
  12. $register->get('redis')->get('redis-cache-2012');
  13. //配置文件写到注册树上
  14. $config = array(
  15. 'db_name' => '127.0.0.1',
  16. );
  17. $register->set('config', $config);
  18. echo $register->get('config')['db_name']; // php > 5.4

这样既实现了单例模式,又使得所有的变量类都统一管理,实现起来更简洁,而且代码效率更高,更省内存。

4. 适配器模式

就是将一些截然不同的函数接口封装成统一的API,最简单的例子就是DB类了。我们有mysql,mysqli,pdo等。如果我们项目中同时有这几种数据库存在,那么做一些操作是很蛋疼的,因为它们的都有各自额api.所以,这个时候,我们可以用适配器模式将3种不同的数据库封装成统一的api, 对外就是统一的方式调用。再比如我们项目中可能会用到的缓存有redis,memcache等,我们也同样可以把他们封装成统一的API接口。

我们用db类为例子来说明下。


  
  1. //4 . 适配器模式
  2. $register = Lib\Register::getInstance();
  3. $config = array(
  4. 'host' => '127.0.0.1',
  5. 'db_name' =>'test',
  6. 'user_name' => 'root',
  7. 'password' => '',
  8. );
  9. //用注册模式将db配置文件注册。
  10. $register->set('db', $config);
  11. //
  12. $register->set('mysql', new Lib\Db\MySQL());
  13. $register->set('mysqli', new Lib\Db\MySQLi());
  14. $register->set('pdo', new Lib\Db\PDO());
  15. //api接口一模一样
  16. $mysqlInfo = $register->get('mysql')->select('select 1+1;');
  17. $mysqliInfo = $register->get('mysqli')->select('select 1+1;');
  18. $pdoInfo = $register->get('pdo')->select('select 1+1;');
  19. var_dump($mysqlInfo, $mysqliInfo, $pdoInfo);
  20. //工厂模式来做也一样
  21. $mysql = Lib\Factory::db('mysql');
  22. $mysqli = Lib\Factory::db('mysqli');
  23. $pdo = Lib\Factory::db('pdo');
  24. //api接口一模一样
  25. $mysqlInfo = $mysql->select('select 1+1;');
  26. $mysqliInfo = $mysqli->select('select 1+1;');
  27. $pdoInfo = $pdo->select('select 1+1;');
  28. var_dump($mysqlInfo, $mysqliInfo, $pdoInfo);

5. 策略模式

6. 数据对象映射模式

7. 观察者模式

8. 原型模式

9. 装饰器模式

10. 迭代器模式

11. 代理模式

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

php中常见的几种设计模式 的相关文章

  • 子目录中的 CakePHP 控制器和模型

    你好 我想知道如何将我的组件和模型放在子目录中 假设我有一个 Web 应用程序 它有 3 个不同的区域 管理员 用户 开发人员 每个区域执行不同的操作并具有完全不同的界面 所以我希望管理员去http www site com admin h
  • 为什么我们要关闭 Mysqli 中的结果

    为什么我们要关闭 result mysqli new mysqli localhost root root test if mysqli gt connect errno echo Failed to connect to MySQL my
  • 根据 WooCommerce 中的订单数量设置折扣

    在WooCommerce中 如何根据订单数量设置折扣 例如 我想根据客户订单应用折扣 首单折扣 50 美元 第二次订购折扣 30 美元 第三次订购折扣10美元 我搜索过互联网但没有找到任何可用的解决方案或插件 Thanks 这是一个挂钩的自
  • 将 mod-rewrite 添加到现有 PHP 网站

    我正在更新一个 php 应用程序 该应用程序当前不使用 url 重写 目的是隐藏文件扩展名 网站总体结构如下 root index php login php page1 php page2 php page3 php page4 php
  • GhostScript PDF 合并(丢失可编辑字段)

    我正在使用 GhostScript 将 PDF 合并为一个 PDF 其中一份 PDF 具有我在 Adob e Acrobat Pro 9 中创建的文本框字段 可编辑字段 当我使用 GhostScript 合并这两个 PDF 时 我丢失了文本
  • 运行 shell 命令并将输出发送到文件?

    我需要能够通过 php 脚本修改我的 openvpn 身份验证文件 我已将我的 http 用户设置为免通 sudoer 因为这台机器仅在我的家庭网络中可用 我目前有以下命令 echo shell exec sudo echo usernam
  • 如何使用 facebook 用户登录我的网站?

    我想知道 facebook 如何让用户登录我们的网站 我的意思是用户需要注册到我的网站才能发表评论 我如何通过我的 php 代码检查它是否是登录用户 我听说你只能用javascript检查它是否是登录用户 感谢您的任何解释 您可以使用脸书
  • PHP 生成文件供下载然后重定向

    我有一个 PHP 应用程序 它创建一个 CSV 文件 强制使用标头下载该文件 这是代码的相关部分 header Content Type application csv header Content length filesize NewF
  • 如何在 Laravel 5.3 中进行自定义身份验证

    我在 Laravel 5 3 自定义身份验证中遇到问题 希望在检查时使用我自己的函数或页面Auth check 它返回false 这是用户控制器 namespace App Http Controllers use App User use
  • PHP session_destroy() 警告会话对象销毁失败[重复]

    这个问题在这里已经有答案了 我有这个 php 脚本 但在破坏会话时遇到问题 我收到这个警告 警告 session destroy 会话对象销毁失败 第 6 行 C xampp htdocs template nota finalizare
  • 如何区分两个同名的多选列表

    对于我正在构建的系统 我需要查看第一个列表中出现了哪些选项 通过 POST 请求 以及第二个列表中出现了哪些选项
  • 正在 WordPress 中上传媒体,但显示错误“上传时发生错误。请稍后重试。”

    我试图在 WordPress 媒体上传中上传 jpeg 图像 上传时 显示错误 上传时发生错误 请稍后再试 上传时出现错误消息 但是当我重新加载页面时 图像显示在列表中 如下所示 重新加载页面后 所以 图片正在上传 这意味着不存在文件夹权限
  • 如何在索引视图中打印关联数据

    subjects this gt Subjects gt find all contain gt Users fields gt Users username Users email gt hydrate false gt toArray
  • 如何从 URL 获取当前的 Web 目录?

    如果我有一个网址http www example com sites dir index html http www example com sites dir index html 我想提取 sites 这个词 我知道我必须使用正则表达式
  • 为什么 count 比 $count 差

    我只是在查看不同问题的答案以了解更多信息 我看到一个answer https stackoverflow com a 4891402 429850这表明在 php 中编写这样的做法是不好的做法 for i 0 i
  • Ace Editor 自动完成和多种语言

    如何为 Ace 编辑器创建自动完成功能以及如何突出显示 php 中的 html javascript 和 csshttp ace ajax org http ace ajax org
  • snappy wkhtmltopdf 包装器将生成的 html 文件发送到浏览器

    我像鼹鼠一样用谷歌搜索 但找不到正确的方法 我正在使用 WKHTMLTOPDF Wrapper Snappy 创建 PDF 如何将使用generateFromHtml方法生成的pdf直接发送到浏览器 这就是我想做的 header Conte
  • 在 JAX-WS 中使用安全性的最佳实践是什么

    这是场景 我有一些需要保护的 Web 服务 JAX WS 目前 为了身份验证需求 我提供了额外的 SecurityWService 它为授权用户提供了一些需要在请求其他服务时描述的 userid 和 sessionid 使用一些java安全
  • CodeIgniter加入选择为

    我的数据库中有 2 个表需要加入 一张表是 artikelen 表 另一张表是 Collections 表 我目前有 this gt db gt select this gt db gt from collecties this gt db
  • Apache LOG:子进程 pid xxxx 退出信号分段错误 (11)

    Apache PHP Mysql Linux 注意 子进程 pid 23145 退出信号分段错误 11 tmp 中可能存在 coredump 但 tmp下没有找到任何东西 我怎样才能找到错误 PHP 代码中函数的无限循环导致了此错误

随机推荐

  • Allegro 铺铜设置

    软件版本 xff1a Allegro16 6 敷铜 xff1a 放置禁止敷铜区域 xff1a Setup Areas Route Keepout 1 标题栏选Shap gt Global Dynamic Params Shape Polyg
  • OVP 过压保护电路

    过压保护电路 OVP 为下游电路提供保护 xff0c 使其免受过高电压的损坏 OVP电路监测外部电源 如 xff1a 离线电源或电池 的直流电压 xff0c 通过下述两种方式中的一种保护后续电路 xff1a 撬棍钳位电路或串联开关 撬棍电路
  • 超全蓝牙芯片原厂总结(含芯片型号)

    转自 xff1a https blog csdn net weixin 42583147 article details 80923946 作者 xff1a XCODER 蓝牙芯片原厂 1 CSR 高通 xff08 被高通收购 xff09
  • ST-Link的internal command error问题的解决方法

    问题 xff1a 显示 xff1a internal command error 这是由于stlink无法识别到芯片的情况 xff0c 通过解决这个问题我找到几个原因和解决方法 xff1a 1 xff0c 芯片睡眠 xff0c 停机 xff
  • 蓝牙 UUID 解释

    一 xff0c 什么是 UUID UUID 可以简单理解为编号 xff0c 唯一的编号 xff0c 用于区分不同的个体 服务和特性都有各自的UUID 比如经典的9527 UUID 就跟身份证一样 xff0c 不管是你是局长还是科长 xff0
  • 【人工智能】传教士和野人问题(M-C问题)

    摘要 本题需要解决的是一般情况下的传教士和野人问题 xff08 M C问题 xff09 通过对问题的一般化 xff0c 我们用一个三元组定义了问题的状态空间 xff0c 并根据约束条件制定了一系列的操作规则 xff0c 最后通过两个启发式函
  • 【算法设计与数据结构】为何程序员喜欢将INF设置为0x3f3f3f3f?

    在算法竞赛中 xff0c 我们常常需要用到一个 无穷大 的值 xff0c 对于我来说 xff0c 大多数时间我会根据具体问题取一个99999999之类的数 xff08 显得很不专业啊 xff01 xff09 在网上看别人代码的时候 xff0
  • 【slighttpd】基于lighttpd架构的Server项目实战(7)—http-parser

    对于http服务器 xff0c http request的解析是比较麻烦的 xff0c 由于我们的重点并不在这上面 xff0c 所以这一部分不打算自己编写 xff0c 而是使用开源的http parser库 xff0c 下面我们将使用该库来
  • select和epoll 原理概述&优缺点比较

    这个问题在面试跟网络编程相关的岗位的时候基本都会被问到 xff0c 刚刚看到一个很好的比喻 xff1a 就像收本子的班长 xff0c 以前得一个个学生地去问有没有本子 xff0c 如果没有 xff0c 它还得等待一段时间而后又继续问 xff
  • 笔记-关于神经网络黑盒模型可解释性,可视化

    原博地址 xff1a 深度学习黑盒可视化指南 xff0c 从隐藏层开始 摘 xff1a 一旦神经网络接收到相当大的所需数据集后 xff0c 该网络就会使用其精确的知识 权重 来证明或识别未知数据样本上的模式 即在经过大量数据集训练以后 xf
  • C++11 多线程 future/promise简介

    1 lt future gt 头文件简介 Classes std future std future error std packaged task std promise std shared futureFunctions std as
  • C++异步调用利器future/promise实现原理

    前言 在异步编程中 xff0c 各种回调将让人眼花缭乱 xff0c 代码分散 xff0c 维护起来十分困难 boost和C 43 43 11 的 future promise 提供了一个很好的解决方案 xff0c 使得代码更加漂亮 易维护
  • 【Heydrones】飞手百科第一篇:一定要看的无人机原理总结

    飞手百科 知识是最好的保险 本文目录 1 xff0c 无人机的飞行原理 2 xff0c 无人机的几大系统 3 xff0c 无人机的外观介绍 4 xff0c 无人机的专业术语 xff08 一 xff09 无人机的飞行原理 旋翼和轮子一样 xf
  • 【Tars】腾讯微服务框架Tars介绍

    目录 1 介绍2 设计思路3 整体架构4 平台特性 1 介绍 Tars是 基于名字服务 使用Tars协议 的高性能 RPC 开发框架 xff0c 同时配套一体化的 服务治理平台 xff0c 帮助个人或者企业快速的以微服务的方式构建自己稳定可
  • C++11常用新特性快速一览

    最近工作中 xff0c 遇到一些问题 xff0c 使用C 43 43 11实现起来会更加方便 xff0c 而线上的生产环境还不支持C 43 43 11 xff0c 于是决定新年开工后 xff0c 在组内把C 43 43 11推广开来 xff
  • 语法糖:萃取lambda表达式

    背景 现在手头主负责的服务代码 xff0c 基本上都用C 43 43 11来开发了 xff0c 异步编程使用的是TAF的future promise future的then函数 xff0c 接受的是一个Callback对象 xff0c 该对
  • hashmap C++实现分析

    一 简介 Map 是 Key Value 对映射的抽象接口 xff0c 该映射不包括重复的键 xff0c 即一个键对应一个值 在HashMap中 xff0c 其会根据hash算法来计算key value的存储位置并进行快速存取 本文介绍的C
  • SpringMVC(04) -- SpringMVC获取请求参数

    SpringMVC学习笔记 源码地址 4 1 xff09 通过ServletAPI获取 将HttpServletRequest作为控制器方法的形参 xff0c 此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的
  • docker删除所有容器/镜像

    1 想要删除容器 xff0c 则要先停止所有容器 xff08 当然 xff0c 也可以加 f强制删除 xff0c 但是不推荐 xff09 xff1a docker stop docker ps a q 2 删除所有容器 docker rm
  • php中常见的几种设计模式

    1 单例模式 单例模式可以说是面向对象语言里最常用 也是最简单的一种模式 单例就是单个实例 xff0c 单个对象的意思 xff0c 就是说我们去实例化一个类的时候 xff0c 不管调用多少次 xff0c 都永远只有一个实例 不会有多个 xf