开发者

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

开发者 https://www.devze.com 2023-04-07 16:56 出处:网络
This question already has answers here: Closed 11 years ago. Possible Duplicate: Dependecy Hell - how does one pass dependencies to deeply nested objects
This question already has answers here: Closed 11 years ago.

Possible Duplicate:

Dependecy Hell - how does one pass dependencies to deeply nested objects

Lately I've been struggling with this particular problem. For testing and managing reasons I decided it would be a better option to inject an object like $config to those who need it. While at start it was ok, later it started polluting the code开发者_如何学运维. For example: Object A uses object B to do its job, object B uses strategy object C, object C uses object D, which needs $config object. So, I have to keep passing $config down this whole chain

In my code I have two objects like that to pass through, which makes constructors big, having duplicated code and generally it smells wrong. I would appreciate any help in refactoring this relationship.


Instead of (pseudo code as general advice) ...

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)
}

you should only pass what is really needed:

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
}

Have your state as local as possible. Global state is the root of an indefinite amount of debugging horror scenarios (as I smell you've just faced).

Alternatively, many classes don't have to know how their objects look like, they are just interested in the public interface. You can apply dependency inversion, then:

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

Using dependency inversion means freeing your classes from needing to know too much. E.g., a Car does not need to know about Trailer and its composition, it just needs to know about CouplingDevice:

trailer        = createTrailer (...)
couplingDevice = createCouplingDevice (...)

car.install (couplingDevice)

couplingDevice.attach (trailer)


It looks like you need to use a singleton or a registry patterns.

The singleton consist of a class (with private constructor) that can be created by a static method in order to obtain the same object (forgive me for the simplification) every time you need it.

It follow this scheme:

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

}

In this way you can obtain the desired object where you need it with something like

$config = Config::getInstance();

without passing it down in your call stack without resorting to globals variables.

The registry has a similar working scheme but let you create a sort of registry, hence the name, of objects you need to make available.


Am I right in thinking that $config contains... well, configuration information that is required by a large portion of your application? If so, it sounds like you should consider the (ubiquitious) singleton design pattern.

If you're not already familiar with it, it is a technique which allows only one instance of a class throughout the run-time of your application. This is very useful when maintaining application-wide values, since you do not run the risk of instantiating a second object; nor are you passing around 'copies' of objects.

As an example, examine the following:

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

?>

You will see that both 'instances' contain the 'property' with value 'abc' since they are both actually the same instance. I apologize if you are already familiar with this technique, but it certainly sounds like the thing you are looking for!

Edit

As pointed out below, this can still be cloned. If you really wanted to prevent this happening, you would have to override the magic method __clone() to stop this happening. The serialization observation is just being pedantic, though.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号