OOP Dependencies: Dependency Injection vs. Registr

2019-04-10 08:33发布

问题:

I know some OOP and have read this and that, but am not a hardcore OOP guy and have no formal training and can't rattle off why something should use dependency injection or not, and may not be able to identify all dependencies in a design, hence my question.

Answering a question here on SO (Using a object in methods of other objects) I started to doubt myself. As far as dependencies, is one of these better or worse or both acceptable? Any design limitations?

I have read and understand both but haven't ever come across a comparison. Why would one be better used in a design, etc.

Dependency Injection:

class myClass {
    private $db;

    public function __construct($db) {
        $this->db = $db;
    }
}

Registry (maybe also something else):

class myClass {
    private $db;

    public function __construct() {
        $this->db = Registry::get('db');
    }
}

回答1:

Both methods are possible implementations of dependency injection. Basically dependency injection means to define a way to configure (inject) the classes to be used at runtime in order to make to code more flexible compared to using using hard coded class names and constructors in the code. Dependency injection follows a principle called inversion of control.

This is especially interesting for testing code but can be used in a variety of other scenarios where you want to make an application flexible and maintainable.

While dependency injection mostly is referred to as DI container, it can generally be done using a couple of different methods - which may also coexist. Here comes an (incomplete) list:

  • using a dependency injection container
  • pass class dependency as constructor or method args
  • using a central registry (as long as it is configurable)
  • using setter methods to set dependencies
  • using configuration files
  • user input
  • ...


回答2:

It has a lot to do with testability.

In your example, the first (Dependency Injection) is better. The reason it's better is because it accepts db as an argument, which means this is testable, you can use a mock db for testing and test myClass does the right thing with it.

The second example (Registry) is not recommended as not only myClass depends on db, it now also depends on Registry, and the worst part it's that it knows how to get a db. It's too smart, it should be dumb like the first one, dumb things are more testable, smart things are not.



回答3:

A dependency injection is required because the object needs certain data to perform its functions correctly.

Dependency Injection can be performed in three different ways:

  • If Object A depends on object B, then you can create object A while creating object B
  • You can use some external class ( like Registry, configuration files etc ) to load the dependency objects, This will decouple the creation of your object from the dependency creation. This is called locator pattern where your object creation is dependent upon the locator to load the objects.
  • You can create the dependency elsewhere and provide it directly to the dependent object. This is called 'manual injection'

Whenever you do any of these, you are already doing dependency injection:

Constructor injection

public class ClassA
{
 // declares the dependency in the constructor

 public function ClassA( dependency1:String )
 {}
}

// and the dependency is created while instantiating the object
var objA:ClassA = new ClassA("Hello World");

Public property injection

public class ClassA
{
 // declares the dependency as a public property
 public var helloStr:String;
}

var obj1:ClassA = new ClassA();

// dependency is fulfilled by setting a public property after instantiation:
obj1.helloStr = "The Zen Master";