Yii2设计模式——注册树模式

注册树模式

注册树模式(Registry Pattern)又叫注册模式、注册器模式。注册树模式通过将对象实例注册到一棵全局的对象树上,需要的时候从对象树上采摘的模式设计方法。

为什么要采用注册树模式?

单例模式在整个项目中创建唯一实例的问题;工厂模式封装了对象的创建方式(工厂方法——用一个抽象方法,抽象工厂——用一簇抽象方法),使得不必总用new关键词去获取对象;创建者模式则是分步骤的创建实例的各个部分;在Yii2中则通过依赖注入容器DI去获取实例...

这些方法实际上都是解决一个问题——如何合理的产生一个对象。但对象既然已经产生出来了,怎么方便的调用这些对象呢?我们在项目内部建立的对象好像散兵游勇一样,不方便统筹管理安排啊。因而注册数模式应运而生。不管你是何种方式产生的对象,都给我“插到”注册树上。我用某个对象的时候,直接从注册树上去取一下就好了,是不是非常方便?注册时模式还为其他模式提供了一种非常好的想法。

代码实现

看看注册树模式的实现:

class Register 
{
    //存放对象的数组
    protected static $objects;
 
    /**
     * 存放一个对象
     * @param $alias
     * @param $object
     */
    public static function set($alias,$object)
    {
        self::$objects[$alias] = $object;
    }
 
    /**
     * 获取一个对象
     * @param $alias
     * @return mixed
     */
    public static function get($alias)
    {
        return self::$objects[$alias];
    }
 
    /**
     * 销毁一个对象
     * @param $alias
     */
    public static function _unset($alias)
    {
        unset(self::$objects[$alias]);
 
    }
}

  

注册树模式很类似服务定位器模式,优点是集中管理,使用方便。缺点是隐藏了对象和对象之间的依赖关系。

Yii2的注册树模式

PHP注册树模式主要用于创建对象的时候将我们的对象与相应的变量进行绑定,从这个角度上说,Yii2的Service Locator和DI Container都用到注册树模式。这二者都在内部维护一个数组(key => value),value为对象或者对象定义,在获取时通过唯一的key来获取,如果是定义再去容器里面实例化一下。

应用举例

在Yii.php中:

<?php

class ServiceLocator extends Component
{
    //保存实例化的对象,每个对象都是单例,且有唯一string类型的ID做区分
    private $_components = [];

    //保存设置的对象或者其定义,用于实例化
    private $_definitions = [];

    //将对象ID作为ServiceLocator的属性,可通过$serviceLocator->{ID}直接获取
    public function __get($name)
    {
        if ($this->has($name)) {
            return $this->get($name);
        }

        return parent::__get($name);
    }

    //检验是否有属性$name
    public function __isset($name)
    {
        if ($this->has($name)) {
            return true;
        }

        return parent::__isset($name);
    }

    //检验是否有对象$id
    public function has($id, $checkInstance = false)
    {
        return $checkInstance ? isset($this->_components[$id]) : isset($this->_definitions[$id]);
    }

    //获取一个对象$id
    public function get($id, $throwException = true)
    {
        //已经实例化的,直接返回
        if (isset($this->_components[$id])) {
            return $this->_components[$id];
        }

        //有该对象的定义,且定义已经是一个对象,设置$_components并直接返回
        if (isset($this->_definitions[$id])) {
            $definition = $this->_definitions[$id];
            if (is_object($definition) && !$definition instanceof Closure) {
                return $this->_components[$id] = $definition;
            }

            //有定义但不是现成对象,则交给DI Container去实例化,并且设置$_components
            return $this->_components[$id] = Yii::createObject($definition);
        } elseif ($throwException) {
            throw new InvalidConfigException("Unknown component ID: $id");
        }

        return null;
    }

    //设置、存放一个对象
    public function set($id, $definition)
    {
        unset($this->_components[$id]);

        if ($definition === null) {
            unset($this->_definitions[$id]);
            return;
        }

        //如果$definition是对象或者类名或者callable,则注册到$_definitions中
        if (is_object($definition) || is_callable($definition, true)) {
            // an object, a class name, or a PHP callable
            $this->_definitions[$id] = $definition;
        } elseif (is_array($definition)) { //如果是带'class'的配置数组,也注册到$_definitions中
            // a configuration array
            if (isset($definition['class'])) {
                $this->_definitions[$id] = $definition;
            } else {
                throw new InvalidConfigException("The configuration for the \"$id\" component must contain a \"class\" element.");
            }
        } else {
            throw new InvalidConfigException("Unexpected configuration type for the \"$id\" component: " . gettype($definition));
        }
    }

    //清楚注册的对象
    public function clear($id)
    {
        unset($this->_definitions[$id], $this->_components[$id]);
    }

}

  

 

posted on 2019-05-13 01:36 追风的浪子 阅读(...) 评论(...) 编辑 收藏

导航