单例模式(Singleton)
模式定义
使应用中只存在一个单例类的实例
UML 图
代码
经常说的三私一公,严格说应该是四私一公。如果想要允许继承,可以去掉
final修饰符,同时将private改为protected
<?phpnamespace 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(){}}
测试
<?phpnamespace 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 图
代码
<?phpnamespace 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(){}}
测试
<?phpnamespace 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);}}
优缺点
多例类与单例类的效果类似
缺点:
难以跟踪依赖关系
难以进行自动化测试
优点:
避免系统中不必要的对象传递
使用多例模式可以节省内存,并确保同一个类的多个实例之间不发生冲突