单例模式(Singleton)
模式定义
使应用中只存在一个单例类的实例
UML 图
代码
经常说的三私一公,严格说应该是四私一公。如果想要允许继承,可以去掉
final
修饰符,同时将private
改为protected
<?php
namespace DesignPatterns\Creational\Singleton;
final class Singleton
{
/**
* @var Singleton
*/
private static $instance = null;
/**
* 通过懒加载获得实例(在第一次使用的时候创建)
*/
public static function getInstance(): Singleton
{
if (null === static::$instance) {
static::$instance = new static();
}
return static::$instance;
}
/**
* 不允许从外部调用以防止创建多个实例
* 要使用单例,必须通过 Singleton::getInstance() 方法获取实例
*/
private function __construct()
{
}
/**
* 防止实例被克隆(这会创建实例的副本)
*/
private function __clone()
{
}
/**
* 防止反序列化(这将创建它的副本)
*/
private function __wakeup()
{
}
}
测试
<?php
namespace DesignPatternsTests\Creational\Singleton;
use PHPUnit\Framework\TestCase;
use DesignPatterns\Creational\Singleton\Singleton;
class SingletonTest extends TestCase
{
public function testUniqueness()
{
$firstCall = Singleton::getInstance();
$secondCall = Singleton::getInstance();
$this->assertInstanceOf(Singleton::class, $firstCall);
$this->assertSame($firstCall, $secondCall);
}
}
优缺点
缺点:
单例类(静态类)和全局变量一样,在系统中的任何地方都可以访问,这使得我们难以跟踪系统中的依赖关系。
使用单例类和全局变量的代码很难使用 PHPUnit 进行自动化测试。全局变量和类的静态属性都是一种全局状态。欲测代码和全局状态之间会强烈耦合,并且其创建无法控制。另外,一个测试对全局状态的改变可能会破坏另外一个测试
优点:
单例模式可以避免系统中不必要的对象传递,适度地使用有助于改善系统的设计
具有良好设计的系统一般都通过方法调用传递对象实例。每个类都会与外部环境保持独立,通过清晰的沟通方式与系统中的其他部分协作。有时这会迫使一些与对象实例无关的类不得不接收它,然后将其传递给真正需要使用它的其他类,结果以良好设计的名义引入了依赖关系
多例模式(Multiton)
模式定义
多例模式是对单例模式的拓展,通过键值对管理一组多例类的实例化对象,对于给定的键值只会存在唯一的实例化对象
UML 图
代码
<?php
namespace DesignPatterns\Creational\Multiton;
final class Multiton
{
const INSTANCE_1 = '1';
const INSTANCE_2 = '2';
/**
* @var Multiton
*/
private static $instances = [];
/**
* 通过指定名称返回实例(使用到该实例的时候才会实例化)
* @param string $instanceName
* @return Multiton
*/
public static function getInstance(string $instanceName): Multiton
{
if (!isset(static::$instances[$instanceName])) {
static::$instances[$instanceName] = new static();
}
return static::$instances[$instanceName];
}
/**
* 不允许从外部调用以防止随意创建对象实例
*/
private function __construct()
{
}
/**
* 防止实例被克隆(这会创建实例的副本)
*/
private function __clone()
{
}
/**
* 防止反序列化(这将创建它的副本)
*/
private function __wakeup()
{
}
}
测试
<?php
namespace DesignPatternsTest\Creational\Multiton;
use DesignPatterns\Creational\Multiton\Multiton;
use PHPUnit\Framework\TestCase;
class MultitonTest extends TestCase
{
public function testUniqueness()
{
$firstCall = Multiton::getInstance(Multiton::INSTANCE_1);
$secondCall = Multiton::getInstance(Multiton::INSTANCE_1);
$this->assertInstanceOf(Multiton::class, $firstCall);
$this->assertSame($firstCall, $secondCall);
}
public function testUniquenessForEveryInstance()
{
$firstCall = Multiton::getInstance(Multiton::INSTANCE_1);
$secondCall = Multiton::getInstance(Multiton::INSTANCE_2);
$this->assertInstanceOf(Multiton::class, $firstCall);
$this->assertInstanceOf(Multiton::class, $secondCall);
$this->assertNotSame($firstCall, $secondCall);
}
}
优缺点
多例类与单例类的效果类似
缺点:
难以跟踪依赖关系
难以进行自动化测试
优点:
避免系统中不必要的对象传递
使用多例模式可以节省内存,并确保同一个类的多个实例之间不发生冲突