Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 

Repository files navigation

依赖注入容器

在面向对象编程中,类与类之间应该“低耦合”,而大多数情况下单一类完成某个职责的情况很少,类之间相互调用才能体现出类的真正强大之处,因此类之间又要有所依赖。

通常类之间依赖通过构造函数或者setter来注入,下面举个简单的栗子。

例如要设计“龟兔赛跑”这么一个故事,首先应该有两个类:兔子类(Rabbit)、乌龟类(Tortoise)。

class Rabbit implements IRabbit {
    public function run() {}
}

class Tortoise implements ITortoise {
    public function crawl() {}
}

然后再设计一个比赛类(Game),它依赖兔子类(Rabbit)和乌龟类(Tortoise),我们以构造函数的方式注入其中以供后续调用。

class Game {

    private $rabbit;
    private $tortoise;

    // 构造函数注入依赖
    public function __construct(IRabbit $rabbit, ITortoise $tortoise) {
        $this->rabbit = $rabbit;
        $this->tortoise = $tortoise;
    }

    public function getResult() {}
}

最后这几个类之间的相互调用来完成“龟兔赛跑”这么一个比赛。

$rabbit = new Rabbit();
$tortoise = new Tortoise();
$game = new Game($rabbit, $tortoise);
$game->getResult();

比赛类Game并没有强依赖于兔子类Rabbit和乌龟类Tortoise,这里仅在构造函数中注入了兔子类Rabbit和乌龟类Tortoise各一个实例,然后调用方法getResult()以获取比赛结果。 在注入过程中,以不同的实例注入,将会得到不同的比赛结果,比如我们注入的兔子实例$rabbit是一只懒惰的兔子,那么比赛结果就是兔子比赛失败。

到这里,上面的例设计子看似完美,其实还存在这么一个问题:就是每次在实例化比赛类Game前,必须要先实例化出兔子类Rabbit和乌龟类Tortoise。如果要在多处实例化Game类,那么这个实例化过程就显得很重复。

换个场景,这样设计的类如果依赖注入有很多,那么其实例化的过程更是显得重复,更如是依赖之间又相互依赖,依赖之间有先后关系,那么情况更是复杂。试想下面的场景:

$component = new Component(
    new A(),
    new B(),
    new C(new D()),
    ...
);

仅仅一个实例化过程,就显得如此的繁琐,更何况还要在多处调用。所以“依赖注入”迫切需要“自动化”来取缔手工注入的造成的重复和麻烦。因此,“依赖注入容器”应运而生。

什么是“依赖注入容器”呢,我的理解,就是将所有依赖都放入一个“容器”中,这个容器能够自动地维护这些依赖关系,从而能够从容器中轻松地取了一个类的实例。比如使用了容器后,下面实例化起来也就显得简单多了。

$di = new Di();
$component = $di->get(Component::className());

这里的/src/Di.php正是这样一个简单的实现。

About

php dependency injection container

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages