Registry pattern and lazy instantiation of registe

2019-04-10 02:42发布

问题:

Let's imagine that we have Registry pattern...

<?php

class Registry
{

private static $objects     = array();
private static $instance    = null;

public static function getInstance() {
    if (self::$instance == null) {
        self::$instance = new Registry();
    }
    return self::$instance;
}

protected function _get($key) {
    return ($this->objects[$key]) ? $this->objects[$key] : null;
}

protected function _set($key, $val) {
    $this->objects[$key] = $val;
}

public static function get($key) {
    return self::getInstance()->_get($key);
}

public static function set($key, $object) {
    return self::getInstance()->_set($key, $object);
}

}
?>

Using this realization is really easy...

<?
Registry::set('db', $db_client);
Registry::set('redis', $redis_client);

//Using registered objects is really easy
Registry::get('db')->query("...");
Registry::get('redis')->get("...");
?>

But as you can see, we're adding instances into registry even if we don't need them (yes, it's all about performance). So, the question is... How to modify Registry pattern to be able to do lazy instantiation?

Here is what I'm looking for...

<?
class Registry
{

private static $objects     = array();
private static $instance    = null;

public static function getInstance() {
    if (self::$instance == null) {
        self::$instance = new Registry();
    }
    return self::$instance;
}

protected function _db() {
    if (!$this->objects['db']) {
        $this->objects['db'] = new DatabaseAdapter(DB_HOST, DB_NAME, DB_USER, DB_PASSWORD);
    }
    return $this->objects['db'];
}

protected function _redis() {
    if (!$this->objects['redis']) {
        $this->objects['redis'] = new Redis(REDIS_HOST, REDIS_DB, REDIS_USER, REDIS_PASSWORD);
    }
    return $this->objects['redis'];
}

public static function db() {
    return self::getInstance()->_db();
}

public static function redis() {
    return self::getInstance()->_redis();
}

}
?>

As you can see, DatabaseAdapter() or Redis() will be created only in we'll request them. Everything seems to be ok, but as you can see it's not a standalone class because _db(), _redis() methods contains connection constants etc. How to avoid it? How can I define registry method within registry class to separate Registy class and objects inside it?

I'm really sorry about my English, but I hope it is clear for you.

Thank you.

PS: All code above was written 1 min. ago and wasn't tested.

回答1:

If you use global constants you will always have a dependency on the global scope. It doesnt matter where it is. Also, even if you do not use constants, you still have the dependency on the Database class inside the Registry. If you want to dissolve those dependencies, you could use Factory methods on the to be created classes:

public function get($service)
{
    if( !this->_data[$service] ) {
        // requires PHP 5.2.3
        this->_data[$service] = call_user_func($service .'::create');
    }
    return this->_data[$service];
}

So if you do get('DB'), the code would try to call the static DB::create() method inside the class you intend to create. But like I said, if you use global Constants for the configuration, you would just move the problem into another class.

Your db class could look like this:

class DB
{
    protected static $_config;
    public static setConfig(array $config)
    {
        self::_config = $config;
    }
    public static create()
    {
        return new self(
            self::config['host'],
            self::config['db'],
            self::config['user'],
            self::config['pass']);
    }
}

The configuration can be stored inside an external configuration file, which you load and set to the DB class during bootstrap, e.g.

DB::setConfig(parse_ini_file('/path/to/db-config.ini'));

The disadvantage of this is, you have to add create() methods all over the place and all classes must be able to store their own configuration. You could centralize these responsibilities into a Builder pattern. But if you do this, you are half way to implementing an IoC Container anyways, so check out the following resources:

  • Fabien Potencier: What is Dependency Injection
  • Martin Fowler: Inversion of Control Containers and the Dependency Injection pattern
  • Design pattern – Inversion of control and Dependency injection


回答2:

Note: You are using a "static" modifier for $objects - as you are working with an instance, this is probaby not necessary.

How can I define registry method within registry class to separate Registy class and objects inside it?

They are always separate: Each object inside the registry class is just a reference to the (independent) object. But if this question is about including the appropriate class definition (?) you may use the class_exists() function to load the class as soon as required.

BurninLeo