一、什么是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仓库,欢迎批评指正!