百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

PHP 单例模式(php单例模式和工厂模式)

moboyou 2025-07-23 17:57 11 浏览

单例模式(Singleton Pattern)

单例模式(Singleton Pattern):顾名思义, 就是只有一个实例。作为对象的创建模式, 单例模式确保某一个类只有一个实例, 而且自行实例化并向整个系统提供这个实例。

(一)为什么要使用PHP单例模式

1, PHP的应用主要在于数据库应用, 一个应用中会存在大量的数据库操作, 在使用面向对象的方式开发时, 如果使用单例模式, 则可以避免大量的new 操作消耗的资源,

还可以减少数据库连接这样就不容易出现 too many connections情况。

2, 如果系统中需要有一个类来全局控制某些配置信息, 那么使用单例模式可以很方便的实现。 这个可以参看zend Framework的FrontController部分。

3, 在一次页面请求中, 便于进行调试, 因为所有的代码(例如数据库操作类db)都集中在一个类中, 我们可以在类中设置钩子, 输出日志, 从而避免到处var_dump, echo

单例模式的实现

1, 私有化一个属性用于存放唯一的一个实例

2, 私有化构造方法, 私有化克隆方法, 用来创建并只允许创建一个实例

3, 公有化静态方法, 用于向系统提供这个实例

<?php
class Singleton{
    //存放实例
    private static $_instance = null;
    //私有化构造方法、
    private function __construct(){
        echo "单例模式的实例被构造了";
    }
    //私有化克隆方法
    private function __clone(){
    }
    //公有化获取实例方法
    public static function getInstance(){
        if (!(self::$_instance instanceof Singleton)){
        self::$_instance = new Singleton();
        }
    		return self::$_instance;
    }
}
$singleton=Singleton::getInstance();
?>
<?php
class Single {
    /**
    * @var Object 保存类实例的静态成员变量
    */
    private static $_instance;
    /**
    * Single constructor. 私有的构造方法
    */
    private function __construct(){
        echo 'This is a Constructed method;';
    }
    /**
    * @purpose: 创建__clone方法防止对象被复制克隆
    */
    public function __clone(){
        //E_USER_ERROR只能通过trigger_error($msg, E_USER_ERROR)手动触发。E_USER_ERROR是用户自定义错误类型,可以被set_error_handler错误处理函数捕获,允许程序继续运行。E_ERROR是系统错误,不能被set_error_handler错误处理函数捕获,程序会退出运行
        trigger_error('Clone is not allow!',E_USER_ERROR);
    }
    /**
    * @return Single|Object 单例方法,用于访问实例的公共的静态方法
    */
    public static function getInstance(){
        if(!(self::$_instance instanceof self)){
        		self::$_instance = new self;
        }
        return self::$_instance;
    }
    /**
    * @purpose: 测试方法
    */
    public function test(){
    		echo '调用方法成功';
    }
}
?>


优点:因为静态方法可以在全局范围内被访问, 当我们需要一个单例模式的对象时, 只需调用getInstance方法, 获取先前实例化的对象, 无需重新实例化。

(二)使用Trait关键字实现类似于继承单例类的功能

Trait Singleton{
    //存放实例
    private static $_instance = null;
    //私有化克隆方法
    private function __clone(){
    }
    //公有化获取实例方法
    public static function getInstance(){
        $class = __CLASS__;
        if (!(self::$_instance instanceof $class)){
            self::$_instance = new $class();
        }
        return self::$_instance;
    }
    }
    class DB {
        private function __construct(){
            echo __CLASS__.PHP_EOL;
        }
    }
class DBhandle extends DB {
    use Singleton;
    private function __construct(){
        echo "单例模式的实例被构造了";
    }
}
$handle=DBhandle::getInstance();

//注意若父类方法为public,则子类只能为pubic,若父类为private,子类为public ,protected,private都可以。

自 PHP 5.4.0 起, PHP 实现了代码复用的一个方法, 称为 traits。

Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 为了减少单继承语言的限制, 使开发人员能够自由地在不同层次结构内独立的类中复用方法集。

Traits 和类组合的语义是定义了一种方式来减少复杂性, 避免传统多继承和混入类(Mixin)相关的典型问题。

补充, 大多数书籍介绍单例模式, 都会讲三私一公, 公优化静态方法作为提供对象的接口, 私有属性用于存放唯一一个单例对象。私有化构造方法, 私有化克隆方法保证只存在一个单例。

但实际上, 虽然我们无法通过 new 关键字和clone出一个新的对象, 但我们若想得到一个新对象。

还是有办法的, 那就是通过序列化和反序列化得到一个对象。私有化sleep()和wakeup()方法依然无法阻止通过这种方法得到一个新对象。

或许真得要阻止, 你只能去__wakeup添加删除一个实例的代码, 保证反序列化增加一个对象, 你就删除一个。不过这样貌似有点怪异。

(三) PHP单例模式及应用场景

单例模式在数据库链接中的使用:

<?php
class Mysql
{
    // MYSQL数据库连接信息
    const HOSTNAME = "127.0.0.1";
    const USERNAME = "root";
    const PASSWORD = "***";
    const DBNAME = "test";
    const CHARSET = "utf8";

    public function MysqlConnect()
    {
        $db = new mysqli(self::HOSTNAME, self::USERNAEM, self::PASSWORD, self::DBNAME); // 连接数据库
        $db->query("set names ".self::CHARSET);
        if (mysqli_connect_errno())
        {
        		throw new MysqlException("服务器系统故障", 1001);
        }
        else
        {
        		return $db;
        }
    }
    }
?>

每次数据库连接都要new这个类, 然后调用mysqlconnect方法, 返回连接, 然后close掉连接, 频繁的new和数据库连接关闭操作非常的消耗资源!数据库软件系统中使用数据库连接池,

主要是节省打开或者关闭数据库连接所引起的效率损耗, 这种效率上的损耗还是非常昂贵的, 因为使用单例模式来维护, 就可以大大降低这种损耗。

因此, 为了避免资源消耗, 我们有了下面的改进:

<?php
class Mysql{
    //该属性用来保存实例
    private static $conn;
    //构造函数为private,防止创建对象
    private function __construct(){
        $this->conn = mysql_connect('localhost','root','');
    }
    //创建一个用来实例化对象的方法
    public static function getInstance(){
        if(!(self::$conn instanceof self)){
            self::$conn = new self;
        }
        return self::$conn;
    }
    //防止对象被复制
    public function __clone(){
        trigger_error('Clone is not allowed !');
    }

}
//只能这样取得实例,不能new 和 clone
$mysql = Mysql::getInstance();
?>

这样就不用每次都来new这个类了, 方便了很多。下面给一个详细的:

<?php
class Mysql
{
private $DB;
static private $_instance;
// 连接数据库
private function __construct($host, $username, $password)
{
    $this->DB = mysql_connect($host, $username, $password);
    $this->query("SET NAMES 'utf8'", $this->link);
    return $this->DB;
}

private function __clone(){}

public static function getInstance($host, $username, $password)
{
    if( !(self::$_instance instanceof self) )
    {
    		self::$_instance = new self($host, $username, $password);
    }
    return self::$_instance;
}

// 连接数据表
public function select_db($database)
{
    $this->result = mysql_select_db($database);
    return $this->result;
}

// 执行SQL语句
public function query($query)
{
		return $this->result = mysql_query($query, $this->link);
}

// 将结果集保存为数组
public function fetch_array($fetch_array)
{
		return $this->result = mysql_fetch_array($fetch_array, MYSQL_ASSOC);
}

// 获得记录数目
public function num_rows($query)
{
return $this->result = mysql_num_rows($query);
}

// 关闭数据库连接
public function close()
{
		return $this->result = mysql_close($this->link);
}

}
?>

使用的时候:

$con = Mysql::getInstance($host, $username, $password);
$con -> select_db($database);


当然, 单例模式不仅仅只是应用在数据库的操作类上面。还可以应用在这些方面:

1. 网站的计数器, 一般也是采用单例模式实现, 否则难以同步。

2. 应用程序的日志应用, 一般都何用单例模式实现, 这一般是由于共享的日志文件一直处于打开状态, 因为只能有一个实例去操作, 否则内容不好追加。

3. Web应用的配置对象的读取, 一般也应用单例模式, 这个是由于配置文件是共享的资源。

PHP单例模式的缺点

众所周知, PHP语言是一种解释型的脚本语言, 这种运行机制使得每个PHP页面被解释执行后, 所有的相关资源都会被回收。也就是说, PHP在语言级别上没有办法让某个对象常驻内存,

这和asp.net、Java等编译型是不同的, 比如在Java中单例会一直存在于整个应用程序的生命周期里, 变量是跨页面级的, 真正可以做到这个实例在应用程序生命周期中的唯一性。

然而在PHP中, 所有的变量无论是全局变量还是类的静态成员, 都是页面级的, 每次页面被执行时, 都会重新建立新的对象, 都会在页面执行完毕后被清空, 这样似乎PHP单例模式就没有什么意义了,

所以PHP单例模式我觉得只是针对单次页面级请求时出现多个应用场景并需要共享同一对象资源时是非常有意义的。

相关推荐

Excel技巧:SHEETSNA函数一键提取所有工作表名称批量生产目录

首先介绍一下此函数:SHEETSNAME函数用于获取工作表的名称,有三个可选参数。语法:=SHEETSNAME([参照区域],[结果方向],[工作表范围])(参照区域,可选。给出参照,只返回参照单元格...

Excel HOUR函数:“小时”提取器_excel+hour函数提取器怎么用

一、函数概述HOUR函数是Excel中用于提取时间值小时部分的日期时间函数,返回0(12:00AM)到23(11:00PM)之间的整数。该函数在时间数据分析、考勤统计、日程安排等场景中应用广泛。语...

Filter+Search信息管理不再难|多条件|模糊查找|Excel函数应用

原创版权所有介绍一个信息管理系统,要求可以实现:多条件、模糊查找,手动输入的内容能去空格。先看效果,如下图动画演示这样的一个效果要怎样实现呢?本文所用函数有Filter和Search。先用filter...

FILTER函数介绍及经典用法12:FILTER+切片器的应用

EXCEL函数技巧:FILTER经典用法12。FILTER+切片器制作筛选按钮。FILTER的函数的经典用法12是用FILTER的函数和切片器制作一个筛选按钮。像左边的原始数据,右边想要制作一...

office办公应用网站推荐_office办公软件大全

以下是针对Office办公应用(Word/Excel/PPT等)的免费学习网站推荐,涵盖官方教程、综合平台及垂直领域资源,适合不同学习需求:一、官方权威资源1.微软Office官方培训...

WPS/Excel职场办公最常用的60个函数大全(含卡片),效率翻倍!

办公最常用的60个函数大全:从入门到精通,效率翻倍!在职场中,WPS/Excel几乎是每个人都离不开的工具,而函数则是其灵魂。掌握常用的函数,不仅能大幅提升工作效率,还能让你在数据处理、报表分析、自动...

收藏|查找神器Xlookup全集|一篇就够|Excel函数|图解教程

原创版权所有全程图解,方便阅读,内容比较多,请先收藏!Xlookup是Vlookup的升级函数,解决了Vlookup的所有缺点,可以完全取代Vlookup,学完本文后你将可以应对所有的查找难题,内容...

批量查询快递总耗时?用Excel这个公式,自动计算揽收到签收天数

批量查询快递总耗时?用Excel这个公式,自动计算揽收到签收天数在电商运营、物流对账等工作中,经常需要统计快递“揽收到签收”的耗时——比如判断某快递公司是否符合“3天内送达”的服务承...

Excel函数公式教程(490个实例详解)

Excel函数公式教程(490个实例详解)管理层的财务人员为什么那么厉害?就是因为他们精通excel技能!财务人员在日常工作中,经常会用到Excel财务函数公式,比如财务报表分析、工资核算、库存管理等...

Excel(WPS表格)Tocol函数应用技巧案例解读,建议收藏备用!

工作中,经常需要从多个单元格区域中提取唯一值,如体育赛事报名信息中提取唯一的参赛者信息等,此时如果复制粘贴然后去重,效率就会很低。如果能合理利用Tocol函数,将会极大地提高工作效率。一、功能及语法结...

Excel中的SCAN函数公式,把计算过程理清,你就会了

Excel新版本里面,除了出现非常好用的xlookup,Filter公式之外,还更新一批自定义函数,可以像写代码一样写公式其中SCAN函数公式,也非常强大,它是一个循环函数,今天来了解这个函数公式的计...

Excel(WPS表格)中多列去重就用Tocol+Unique组合函数,简单高效

在数据的分析和处理中,“去重”一直是绕不开的话题,如果单列去重,可以使用Unique函数完成,如果多列去重,如下图:从数据信息中可以看到,每位参赛者参加了多项运动,如果想知道去重后的参赛者有多少人,该...

Excel(WPS表格)函数Groupby,聚合统计,快速提高效率!

在前期的内容中,我们讲了很多的统计函数,如Sum系列、Average系列、Count系列、Rank系列等等……但如果用一个函数实现类似数据透视表的功能,就必须用Groupby函数,按指定字段进行聚合汇...

Excel新版本,IFS函数公式,太强大了!

我们举一个工作实例,现在需要计算业务员的奖励数据,右边是公司的奖励标准:在新版本的函数公式出来之前,我们需要使用IF函数公式来解决1、IF函数公式IF函数公式由三个参数组成,IF(判断条件,对的时候返...

Excel不用函数公式数据透视表,1秒完成多列项目汇总统计

如何将这里的多组数据进行汇总统计?每组数据当中一列是不同菜品,另一列就是该菜品的销售数量。如何进行汇总统计得到所有的菜品销售数量的求和、技术、平均、最大、最小值等数据?不用函数公式和数据透视表,一秒就...