Yii2设计模式——单例模式
应用举例
在yii.php中:
require __dir__ . '/baseyii.php'; // yii框架的帮助类,提供框架基本的功能 class yii extends \yii\baseyii { } spl_autoload_register(['yii', 'autoload'], true, true); yii::$classmap = require __dir__ . '/classes.php'; // 只在入口脚本require '../yii.php'时创建一个container实例 yii::$container = new yii\di\container();
在baseyii中相关的部分是,
<?php class baseyii { //yii类维持一个di container的单例 public static $container; //... // 在yii框架中,除了少数情况,都是通过yii::createobject()来创建类的实例 // 而在yii::createobject()中始终用到一个单例yii::$container public static function createobject($type, array $params = []) { if (is_string($type)) { return static::$container->get($type, $params); } elseif (is_array($type) && isset($type['class'])) { $class = $type['class']; unset($type['class']); return static::$container->get($class, $params, $type); } elseif (is_callable($type, true)) { return static::$container->invoke($type, $params); } elseif (is_array($type)) { throw new invalidconfigexception('object configuration must be an array containing a "class" element.'); } throw new invalidconfigexception('unsupported configuration type: ' . gettype($type)); } //... }
这里使用了单例模式。
单例模式
模式定义
单例模式确保一个类只有一个实例,并提供一个全局访问点。当现实中只需要一个对象,或者为了节省系统资源,又或者是为了共享数据的时候可以使用单例模式。
代码实现
我们先来看看单例模式的标准实现:
final class singleton { /** * @var singleton * 维持一个对自身的引用,并保证其唯一性 */ private static $instance; // 获取实例唯一的入口 public static function getinstance(): singleton { if (null === static::$instance) { static::$instance = new static(); } return static::$instance; } // 不允许通过new的方式产生,只能通过singleton::getinstance()方法 private function __construct() { } // 也不允许clone()方法,此方法也会产生一个新的实例 private function __clone() { } // 也不允许反序列化,因为反序列化也会产生一个新的实例 private function __wakeup() { } }
单例模式不允许产生单例的类被继承,不允许通过new方式产生,除了规定的getinstance()方法,别的实例化的途径基本被堵死。而在类的内部维持一个对自身的引用,并保证其是唯一的。
单例模式估计是所有涉及模式中最简单的了,在php和yii中很少见到直接这么使用的,更多的是其变化的形式。
yii的单例模式
yii使用单例的场景非常多,比如请求开始创建的application,yii,request,response等对象功能都十分丰富且开销也很大,维持一个单例就可供请求的整个生命周期使用。在请求开始即创建,请求结束自行销毁,中间不销毁也不创建。这些对象使用了单例没有疑问,但是这些单例的产生、管理和使用却是有不同讲究的。
对象如何创建又如何维护,恐怕任何一个php框架都绕不开这个问题。yii2采用服务定位器和依赖注入容器来提供大部分对象。在容器中使用单例好处是非常明显的。至少可以表现在节省内存和公用组件方面。
节省内存
yii::$container
在内存中仅有一份,所有使用di容器的场合(application/module等)都用到这个di容器。 这就节省了大量的内存空间和反复构造实例的时间。
共用组件
更为重要的是,di容器的单例化,使得yii不同的模块共用组件成为可能。 可以想像,由于共用了di容器,容器里面的内容也是共享的。因此,你可以在a模块中改变某个组件的状态,而b模块中可以了解到这一状态变化。 但是,如果不采用单例模式,而是每个模块(application/module)都维护一个自己的di容器, 要实现这一点难度会大得多。所以,这种共享di容器的设计是必然的、合理的。
上一篇: PyTorch环境搭建及踩过的坑