您的位置:

深入解析PHP单例模式

一、什么是PHP单例模式

单例模式是一种常用的软件设计模式,它保证某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式的实现很简单,只需要将类的构造方法设为私有,这样该类就无法在外部被实例化,再创建一个静态的方法作为实例化该类的入口。


class Singleton
{
    private static $instance = null;

    private function __construct()
    {

    }

    public static function getInstance()
    {
        if (null === static::$instance) {
            static::$instance = new static();
        }

        return static::$instance;
    }
}

上面代码中,$instance是当前类的实例,使用getInstance()方法得到实例,若还没有存在实例,new一个实例然后返回。由于每次返回的均为同一实例,故称单例。PS:这里我们采用了static来实现一些静态绑定的操作,让单例类适用于面向对象和继承特性。如果你对于static关键字不熟悉,建议先查看与学习。

二、PHP单例模式的优点

单例模式具有以下优点:

1. 节省内存空间: 如果全局变量在某个时刻不再使用,但其仍然占据着内存空间,如果这个全局变量被赋值为NULL或者是Session中某个变量删除了这个变量,此时它在内存中占用的空间并没有被释放。而单例模式的实例在全局只需保存一次,对于频繁使用的对象可以大大节省内存空间。

2. 简化调用: 单例模式使得代码调用更加方便,而不必担心实例化问题。单例模式的每个请求获得同一个对象实例,节省了资源。

3. 保证数据一致性: 在程序运行期间,单例模式可以保证内存中只有一个对象,可以很好地控制实例化次数,从而保证数据的一致性。

三、PHP单例模式的缺点

单例模式虽然便捷,但有时会给我们带来一些问题:

1. 开销大: 大部分情况下,单例模式的性能要比普通实例化的开销要大,这主要是由于在调用其getInstance()方法时,必须先检查是否存在一个实例并进行实例化,从而让实例化过程变慢。

2. 修改困难: 在一个系统中,单例的优势变得明显,但是当一个系统需要许多单例类时,修改成本会变得更高。因为所有使用该单例类的代码都需要改动。

3. 不利于测试: 单例模式跟全局变量很像,都是不受控制的!由于单例模式限制了构造函数(如果Singleton类被禁止以常规方式进行实例化),这使得单例模式变得难以测试。它们很难被mock或者替换为另一个因为很难保证测试是否与实际代码可以正常运行,这使得测试变得困难。

四、什么时候适合用PHP单例模式

1. 数据库连接: PHP的数据库连接需要较长时间,使用单例模式可以减少连接时间和不必要的流量。

2. 日志文件: 多人开发过程中,如果频繁输出同一个文件,会导致系统不断地锁定和释放文件,造成很大的系统性能消耗。使用单例模式可以避免多次打开同一文件。

3. 只需一个对象实例: 如果在程序中只需要一个对象,那么使用单例模式可以减小开销。

五、PHP单例模式的应用场景

PHP单例模式广泛用于数据处理和资源管理方面,这里我们来介绍两个典型的应用场景。

1、数据库连接池

为了避免频繁开启数据库连接和释放数据库连接,很多开发人员选择使用数据库连接池来管理数据库连接。连接池是一种典型的单例模式。在连接池中,只有一个对象实例,可以重复使用该实例。


class DatabaseConnection
{
    private static $instance = null;
    private $connection = null;

    private function __construct()
    {
        $this->connection = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
    }

    public static function getInstance()
    {
        if (null === static::$instance) {
            static::$instance = new static();
        }

        return static::$instance;
    }

    public function getConnection()
    {
        return $this->connection;
    }
}

上面代码中,getInstance()方法用于获取DatabaseConnection类的唯一实例,getConnection()方法用于获取数据库连接对象。

2、配置信息

PHP的配置信息经常需要被修改,所以使用单例模式将配置信息存储在内存中,并注入到应用程序中,可以方便地在应用程序中使用配置信息。


class Config
{
    private static $instance = null;
    private $config = [];

    private function __construct()
    {
        $this->config = parse_ini_file('config.ini');
    }

    public static function getInstance()
    {
        if (null === static::$instance) {
            static::$instance = new static();
        }

        return static::$instance;
    }

    public function get($key)
    {
        if (isset($this->config[$key])) {
            return $this->config[$key];
        }

        return false;
    }
}

上面代码中,getInstance()方法用于获取Config类的唯一实例,get()方法用于获取配置信息。

六、小结

相对于常规对象生成方式,在某些场合,单例模式的优点尤为突出。PHP单例模式解决了一些常见问题,但亦会带来新问题,需细心思考,做出最实用、高效的代码设计。

全文源码已放置于Github仓库,欢迎批评指正!