企业架构之外的基础模式
注册表
注册表的作用是提供系统级别的对象访问
注册表提供静态方法(或单例对象的实例化方法)来让其他对象访问其中的数据(通常是对象)。整个系统中的每个对象都可以访问这些数据对象。
实现
下面是代码:
<?php
class Registry
{
private static $instance;
private $request;
private function __construct()
{
}
static function instance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
function getRequest()
{
return $this->request;
}
function setRequest(Request $request)
{
$this->request = $request;
}
}
// empty class for testing
class Request
{
}
$reg = Registry::instance();
$reg->setRequest(new Request());
$reg = Registry::instance();
print_r($reg->getRequest());
?>
代码通过instance()方法创建并返回Registry类的单个实例,然后用于设置和获取Request对象。
我们还可以改写注册表类,基于键来存储和获取数据:
<?php
class Registry
{
private static $instance;
private $values = array();
private function __construct()
{
}
static function instance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
function get($key)
{
if (isset($this->values[$key])) {
return $this->values[$key];
}
return null;
}
function set($key, $value)
{
$this->values[$key] = $value;
}
}
// empty class for testing
class Request
{
}
$reg = Registry::instance();
$reg->set('request', new Request());
$reg = Registry::instance();
print_r($reg->get('request'));
?>
这样做的好处是不需要为希望存储和访问的每个对象都创建类方法。坏处就是重新引入了全局变量。
你也可以在系统中使用注册表对象作为普通对象的工厂。注册表对象对于测试也很有用。
<?php
class Registry
{
private static $instance;
private $request;
private $treeBuilder;
private static $testmode = false;
private function __construct()
{
}
static function testMode($mode = true)
{
self::$instance = null;
self::$testmode = $mode;
}
static function instance()
{
if (self::$testmode) {
return new MockRegistry();
}
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
function getRequest()
{
return $this->request;
}
function setRequest(Request $request)
{
$this->request = $request;
}
function treeBuilder()
{
if (!isset($this->treeBuilder)) {
$this->treeBuilder = new TreeBuilder($this->conf()->get('treedir'));
}
return $this->treeBuilder;
}
function conf()
{
if (!isset($this->conf)) {
$this->conf = new Conf();
}
return $this->conf;
}
}
class Conf
{
function get()
{
}
}
class TreeBuilder
{
}
class MockRegistry
{
}
// empty class for testing
class Request
{
}
$reg = Registry::instance();
$reg->setRequest(new Request());
$reg2 = Registry::instance();
print_r($reg2->getRequest());
print_r($reg2->treeBuilder());
// testing the system
Registry::testMode();
$mockreg = Registry::instance();
print_r($mockreg);
Registry::testMode(false);
$reg4 = Registry::instance();
print_r($reg4);
?>
注册表、作用域和PHP
作用域通常用于描述代码结构中对象或值的可见程度。标准级别是指一个http请求从开始到结束的周期。
<?php
namespace woo\base;
abstract class Registry
{
abstract protected function get($key);
abstract protected function set($key, $val);
}
class RequestRegistry extends Registry
{
private $values = array();
private static $instance;
private function __construct()
{
}
static function instance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
protected function get($key)
{
if (isset($this->values[$key])) {
return $this->values[$key];
}
return null;
}
protected function set($key, $val)
{
$this->values[$key] = $val;
}
static function getRequest()
{
return self::instance()->get('request');
}
static function setRequest(\woo\controller\Request $request)
{
return self::instance()->set('request', $request);
}
}
class SessionRegistry extends Registry
{
private static $instance;
private function __construct()
{
session_start();
}
static function instance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
protected function get($key)
{
if (isset($_SESSION[__CLASS__][$key])) {
return $_SESSION[__CLASS__][$key];
}
return null;
}
protected function set($key, $val)
{
$_SESSION[__CLASS__][$key] = $val;
}
function setComplex(Complex $complex)
{
self::instance()->set('complex', $complex);
}
function getComplex()
{
return self::instance()->get('complex');
}
}
class ApplicationRegistry extends Registry
{
private static $instance;
private $freezedir = "data";
private $values = array();
private $mtimes = array();
private function __construct()
{
}
static function instance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
protected function get($key)
{
$path = $this->freezedir . DIRECTORY_SEPARATOR . $key;
if (file_exists($path)) {
clearstatcache();
$mtime = filemtime($path);
if (!isset($this->mtimes[$key])) {
$this->mtimes[$key] = 0;
}
if ($mtime > $this->mtimes[$key]) {
$data = file_get_contents($path);
$this->mtimes[$key] = $mtime;
return ($this->values[$key] = unserialize($data));
}
}
if (isset($this->values[$key])) {
return $this->values[$key];
}
return null;
}
protected function set($key, $val)
{
$this->values[$key] = $val;
$path = $this->freezedir . DIRECTORY_SEPARATOR . $key;
file_put_contents($path, serialize($val));
$this->mtimes[$key] = time();
}
static function getDSN()
{
return self::instance()->get('dsn');
}
static function setDSN($dsn)
{
return self::instance()->set('dsn', $dsn);
}
}
class MemApplicationRegistry extends Registry
{
private static $instance;
private $values = array();
private $id;
const DSN = 1;
private function __construct()
{
$this->id = shm_attach(55, 10000, 0600);
if (!$this->id) {
throw new Exception("could not access shared memory");
}
}
static function instance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
protected function get($key)
{
return shm_get_var($this->id, $key);
}
protected function set($key, $val)
{
return shm_put_var($this->id, $key, $val);
}
static function getDSN()
{
return self::instance()->get(self::DSN);
}
static function setDSN($dsn)
{
return self::instance()->set(self::DSN, $dsn);
}
}
class AppException extends \Exception
{
}
?>
在例子中我们使用了命名空间,使用包的层次结构并且利用命名空间为项目带来的简洁性和清晰性是有道理的。
SessionRegistry和ApplicationRegistry都将数据序列化后保存到文件系统,因此有件事非常重要:从不同请求取回的对象是同一对象的不同副本,而不是对同一对象的引用。
注意的一点是并不是所有的对象都适合序列化输出。特别是资源类型的数据(如何数据库连接句柄),它们无法被序列化。
最后,Registry对象最好不要存储过多的数据,里面的数据集合也要经过良好定义。