Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在PHP中实现注册表模式的可伸缩方式?_Php_Design Patterns_Registry - Fatal编程技术网

在PHP中实现注册表模式的可伸缩方式?

在PHP中实现注册表模式的可伸缩方式?,php,design-patterns,registry,Php,Design Patterns,Registry,我想知道是否有一种在PHP中实现注册表模式的好方法,让我更清楚地说: 我确实知道,当您需要跟踪实例化的对象以重用它们,而不是从一个脚本到另一个脚本再次实例化它们时,可以使用注册表。例如,我有一个数据库类,我只想实例化一次,然后用于我的所有脚本,我不想一次又一次地重新实例化它。另一个示例可以是表示当前登录用户的实例的用户类。在这种情况下,我不能使用单例,因为例如,当我想检索当前登录用户的朋友时,我需要另一个用户实例等 因此,我提出了这样一个想法:在这种情况下,注册中心更适合这种需要 我还知道有两种

我想知道是否有一种在PHP中实现注册表模式的好方法,让我更清楚地说:

我确实知道,当您需要跟踪实例化的对象以重用它们,而不是从一个脚本到另一个脚本再次实例化它们时,可以使用注册表。例如,我有一个数据库类,我只想实例化一次,然后用于我的所有脚本,我不想一次又一次地重新实例化它。另一个示例可以是表示当前登录用户的实例的用户类。在这种情况下,我不能使用单例,因为例如,当我想检索当前登录用户的朋友时,我需要另一个用户实例等

因此,我提出了这样一个想法:在这种情况下,注册中心更适合这种需要

我还知道有两种实现方法,或者更好的两种方法来访问存储的实例:

  • 显式地或外部地,这意味着每次需要在脚本中恢复实例或需要在其中放置实例时,都应该调用注册表
  • 隐式地或内部地,这意味着您使用getInstance()方法创建一种抽象类,该方法使用get_called_class()后期静态绑定功能返回一个实例,将其添加到注册表,然后从注册表本身返回该实例,注意如果$label参数传递给getInstance()方法,然后,将返回注册表中的特定实例。这种方法对消费者来说是透明的,而且在我看来更干净、更整洁(不过,我将展示这两种实现)
让我们看一个基本的注册表(非常简单的实现,只是一本书中的一个示例):

我知道,注册表可以是一个单例,所以您永远不会同时拥有两个注册表(谁需要它们,谁会想到,但谁知道)。 无论如何,外部存储/访问实例的方式如下:

$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);
  }
}
显然,间接引用注册表(第二种情况)对我来说似乎更具可伸缩性,但如果有一天我需要更改注册表并使用另一个实现,我需要更改getInstance()方法中对registry::get()和registry::set()的调用以适应这些更改,或者有更智能的方法吗

你们中是否有人遇到过这个问题,并根据应用程序的类型、复杂性等找到了一种简单的方法来交换不同的注册表

解决方案应该是一个配置类吗?或者,如果可能的话,有没有更智能的方法来实现可伸缩的注册表模式


谢谢你的关注!希望得到一些帮助

首先。你自己发现了方法的问题,这太好了。通过使用注册表,您可以将类紧密耦合到从中提取依赖项的注册表。不仅如此,如果您的类必须关心它们如何存储在注册表中并从注册表中获取(在您的情况下,每个类也将实现一个单例),那么您也违反了

根据经验法则,请记住:从任何存储设备全局访问类中的对象都会导致类和存储设备之间的紧密耦合。

让我们看看:

关键区别在于,使用服务定位器,服务的每个用户都依赖于定位器。定位器可以隐藏对其他实现的依赖关系,但您确实需要查看定位器。所以定位器和注入器之间的决定取决于这种依赖性是否是一个问题

使用服务定位器,您必须在源代码中搜索对定位器的调用。具有查找引用功能的现代IDE使这变得更容易,但它仍然不如查看构造函数或设置方法那么容易

所以你看这取决于你在建什么。如果你有一个依赖性很低的小应用程序,去他妈的,继续使用注册表(但你绝对应该删除一个类行为来将自己存储到注册表中或从注册表中抓取)。如果情况并非如此,并且您正在构建复杂的服务,并且希望有一个干净、直观的API,那么可以通过使用类型提示和构造函数注入显式地定义依赖项

<?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);

<?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);