我想知道是否有一种在PHP中实现注册表模式的好方法,让我更清楚地说明:
我确实知道,当您需要跟踪您实例化的对象时,就会使用注册表,以便重用它们,而不是从一个脚本重新实例化它们,例如,我有一个数据库类,我只想一次实例化它,然后对我的所有脚本使用它,我不想一次又一次地重新实例化它。另一个示例可以是表示当前登录用户的实例的user类。在这种情况下,我无法使用Singleton,因为例如,当我想检索当前登录用户的朋友时,我需要另一个用户实例。
因此,我想出了一个想法,即书记官处更适合这种情况下的需要。
我还知道有两种方法来实现它,或者更好的两种方法来访问存储的实例:
让我们来看看基本注册表(非常简单的实现,只是从一本书中摘取的一个例子):
class Registry {
static private $_store = array();
static public function set($object, $name = null)
{
// Use the class name if no name given, simulates singleton
$name = (!is_null($name)) ? $name: get_class($object);
$name = strtolower($name);
$return = null;
if (isset(self::$_store[$name])) {
// Store the old object for returning
$return = self::$_store[$name];
}
self::$_store[$name]= $object;
return $return;
}
static public function get($name)
{
if (!self::contains($name)) {
throw new Exception("Object does not exist in registry");
}
return self::$_store[$name];
}
static public function contains($name)
{
if (!isset(self::$_store[$name])) {
return false;
}
return true;
}
static public function remove($name)
{
if (self::contains($name)) {
unset(self::$_store[$name]);
}
}
}我知道,Registry可能是一个Singleton,所以您从来没有同时拥有两个注册表(谁需要它们,谁可以思考,但谁知道)。无论如何,外部存储/访问实例的方法如下:
$read = new DBReadConnection;
Registry::set($read);
$write = new DBWriteConnection;
Registry::set($write);
// To get the instances, anywhere in the code:
$read = Registry::get('DbReadConnection');
$write = Registry::get('DbWriteConnection');在内部,当调用getInstance时,类内(从书中摘录):
abstract class DBConnection extends PDO {
static public function getInstance($name = null)
{
// Get the late-static-binding version of __CLASS__
$class = get_called_class();
// Allow passing in a name to get multiple instances
// If you do not pass a name, it functions as a singleton
$name = (!is_null($name)) ?: $class;
if (!Registry::contains($name)) {
$instance = new $class();
Registry::set($instance, $name);
}
return Registry::get($name);
}
}
class DBWriteConnection extends DBConnection {
public function __construct()
{
parent::__construct(APP_DB_WRITE_DSN, APP_DB_WRITE_USER, APP_DB_WRITE_PASSWORD);
} }
class DBReadConnection extends DBConnection {
public function __construct()
{
parent::__construct(APP_DB_READ_DSN, APP_DB_READ_USER,APP_DB_READ_PASSWORD);
}
}显然,间接引用注册表(第二种情况)对我来说似乎更具有可伸缩性,但是如果有一天我需要更改注册表并使用另一种实现,那么我需要更改对registry的调用:get()和Registry::getInstance()方法中的set(),以适应这些更改,或者有更明智的方法吗?
你们中有人遇到这个问题了吗?他们找到了一种简单的方法来交换不同的注册中心,这取决于应用程序的类型和复杂性等等。
应该是一个配置类的解决方案吗?或者,如果可能的话,是否有更明智的方法来实现可伸缩的注册表模式?
谢谢大家的关注!希望能得到帮助!
发布于 2014-07-15 08:53:00
首先。很高兴你自己发现了你的方法的问题。通过使用注册表,您可以将类紧密耦合到从中提取依赖项的注册表。不仅如此,如果您的类必须关心它们如何存储在注册表中并从注册表中被抓取(在您的例子中,每个类也实现了一个单例),那么您也违反了单一责任原则。
记住:从类内从任何存储中全局访问对象将导致类和存储之间的紧密耦合。
让我们看看马丁·福勒对这个话题有什么看法?
关键的区别在于,使用Service,服务的每个用户都依赖于定位器。定位器可以隐藏与其他实现的依赖关系,但您确实需要查看定位器。因此,定位器和注入器之间的决定取决于这种依赖是否是一个问题。
和
使用服务定位器,您必须在源代码中搜索对定位器的调用。具有find引用功能的现代IDE使这更容易,但它仍然不像查看构造函数或设置方法那么容易。
所以你看,这取决于你在建造什么。如果您有一个小应用程序,依赖程度很低,那就去死吧,继续使用注册表(但是您绝对应该放弃一个类行为来将自己存储到注册表中,或者从注册表中抓取)。如果不是这样的话,并且您正在构建复杂的服务,您需要一个干净而直观的API,通过使用Type提示和Constructor注入显式地定义依赖项。
<?php
class DbConsumer {
protected $dbReadConnection;
protected $dbWriteConnection;
public function __construct(DBReadConnection $dbReadConnection, DBWriteConnection $dbWriteConnection)
{
$this->dbReadConnection = $dbReadConnection;
$this->dbWriteConnection = $dbWriteConnection;
}
}
// You can still use service location for example to grab instances
// but you will not pollute your classes itself by making use of it
// directly. Instead we'll grab instances from it and pass them into
// the consuming class
// [...]
$read = $registry->get('dbReadConnection');
$write = $registry->get('dbWriteConnection');
$dbConsumer = new DbConsumer($read, $write);应该是一个配置类的解决方案吗?或者,如果可能的话,是否有更明智的方法来实现可伸缩的注册表模式?
这种方法经常遇到,您可能听说过一些关于DI-容器的内容。Fabien Potencier写了以下文章
依赖注入容器是一个知道如何实例化和配置对象的对象。为了能够完成它的工作,它需要知道构造函数参数和对象之间的关系。
服务定位器和DI容器之间的界限似乎很模糊,但我喜欢这样想:服务定位器隐藏类的依赖关系,而DI容器不隐藏依赖项(这与简单单元测试的好处一样)。
所以你看,没有最终的答案,这取决于你在建设什么。我可以建议更深入地研究这个主题,因为如何管理依赖关系是每个应用程序的核心关注点。
进一步阅读
https://stackoverflow.com/questions/24742383
复制相似问题