如何避免到处传递上下文对象


How do I avoid passing context object all over the place?

可能重复:
Dependency Hell-如何将依赖关系传递给深度嵌套对象

最近我一直在努力解决这个特殊的问题。出于测试和管理的原因,我决定向需要的人注入$config这样的对象会是一个更好的选择。虽然一开始还可以,但后来它开始污染代码。例如:对象A使用对象B来完成它的工作,对象B使用策略对象C,对象C使用对象D,对象D需要$config对象。所以,我必须不断地把$config传给整个链

在我的代码中,我有两个类似的对象要通过,这使得构造函数很大,有重复的代码,而且通常闻起来是错误的。如果能为重构这种关系提供任何帮助,我将不胜感激。

代替(伪代码作为一般建议(。。。

config <-- ...
A.constructor (config) {
   this.foo = config.foo
   this.bar = config.bar
   this.objectB = createB (config)
}
B.constructor (config) {
   this.frob = config.frob
   this.objectC = createC (config)
}
C.constructor (config) {
   this.frobnicate = config.frobnicate
   this.objectD = createC (configD)
}

你应该只通过真正需要的:

config <-- ...
A.constructor (foo, bar, frob, frobnicate) {
   this.foo = foo
   this.bar = bar
   this.objectB = createB (frob, frobnicate)
}
B.constructor (frob, frobnicate) {
   this.frob = frob
   this.objectC = createC (frobnicate)
}
C.constructor (frobnicate) {
   this.frobnicate = frobnicate
}

让你所在的州尽可能地本地化。全局状态是无数调试恐怖场景的根源(我听说你刚刚遇到过(。

或者,许多类不必知道它们的对象是什么样子的,它们只对公共接口感兴趣。您可以应用依赖反转,然后:

config <-- ...
objectC = createC (config.frobnicate)
objectB = createB (config.frob, objectC)
objectA = createA (config.foo, config.bar, objectB)

使用依赖反转意味着让类不需要知道太多。例如,一个Car不需要知道Trailer及其组成,它只需要了解CouplingDevice:

trailer        = createTrailer (...)
couplingDevice = createCouplingDevice (...)
car.install (couplingDevice)
couplingDevice.attach (trailer)

看起来您需要使用singleton或注册表模式。

singleton由一个类(带有私有构造函数(组成,该类可以通过静态方法创建,以便在每次需要时获得相同的对象(请原谅我的简化(。

它遵循这个方案:

class Config {
    static private instance=null;
    private function __constructor() {
      // do your initializzation here
    }
    static public function getInstance() {
      if (self::instance==null) {
        self::instance== new Config();
      }
      return self::instance;
    }
    // other methods and properties as needed

}

通过这种方式,你可以用之类的东西在需要的地方获得所需的对象

$config = Config::getInstance();

而不用在调用堆栈中传递它,也不用求助于全局变量。

注册表有一个类似的工作方案,但允许您创建一种注册表,从而为需要提供的对象命名。

我认为$config包含。。。那么,您的应用程序的大部分都需要的配置信息?如果是这样的话,听起来你应该考虑(无处不在的(单例设计模式。

如果您还不熟悉它,那么它是一种在应用程序的整个运行时只允许类的一个实例的技术。这在维护应用程序范围的值时非常有用,因为您不会冒实例化第二个对象的风险;也没有传递对象的"副本"。

例如,检查以下内容:

<?php
class Test {
  private static $instance;
  private function __construct() {  } // Private constructor
  static function instance() {
    if (!isset(self::$instance)) {
       self::$instance = new self();
    }
    return self::$instance;
  }
}
$a = Test::instance();
$b = Test::instance();
$a->property = 'abc';
var_dump($a->property);
var_dump($b->property);
?>

您将看到两个"instance"都包含值为"abc"的"property",因为它们实际上都是同一个实例。如果你已经熟悉这个技巧,我很抱歉,但这听起来确实是你想要的东西!

编辑

正如下面所指出的,这仍然可以被克隆。如果你真的想防止这种情况发生,你必须重写魔术方法__clone((来阻止这种情况的发生。不过,序列化观察只是迂腐。