前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Dependency Injection 和 Service Locator

Dependency Injection 和 Service Locator

作者头像
LA0WAN9
发布2021-12-14 08:27:01
4600
发布2021-12-14 08:27:01
举报
文章被收录于专栏:火丁笔记

如果说学院派的 Java 程序员骨子里都浸淫着学究范儿的话,那么游击队出身的 PHP 程序员则从头到脚洋溢着乡土气息。通常他们不太在意理论,一切以实现为先,虽然这样的做法在项目早期能获得不错的收益,但是随着项目的推进,复杂度的提升,缺乏理论基础的弊端终将显现。好在 PHP 社区没有裹足不前,比如说十几年前 Java 社区中流行的 IoC 概念,最近一两年终于被 PHP 社区所接纳。

说起 IoC,其实是 Inversion of Control 的缩写,翻译成中文叫控制反转,不得不说这个名字起得让人丈二和尚摸不着头脑,实际上简而言之它的意思是说对象之间难免会有各种各样的依赖关系,如果我们的代码直接依赖于具体的实现,那么就是一种强耦合,从而降低了系统的灵活性,为了解耦,我们的代码应该依赖接口,至于具体的实现,则通过第三方注入进去,这里的第三方通常就是我们常说的容器。因为在这个过程中,具体实现的控制权从我们的代码转移到了容器,所以称之为控制反转。

如果你看过 Martin Fowler 关于 IoC 的介绍,那么你就会知道 IoC 是一个宽泛的概念,具体点儿说有两种不同的实现方式,分别是:Dependency InjectionService Locator。现在很多 PHP 框架都实现了容器,比如 Phalcon (1),Yii (1)(2),Laravel (1)(2) 等。

至于 Dependency Injection 和 Service Locator 的区别,与其说一套云山雾绕的概念,不能给出几个鲜活的例子来得自然,为了偷懒,我直接套用 TheKeyboard 的文章:

如果没有容器,那么 Dependency Injection 看起来就像:

代码语言:javascript
复制
<?php

class Foo
{
    protected $_bar;
    protected $_baz;

    public function __construct(Bar $bar, Baz $baz) {
        $this->_bar = $bar;
        $this->_baz = $baz;
    }
}

// In our test, using PHPUnit's built-in mock support
$bar = $this->getMock('Bar');
$baz = $this->getMock('Baz');

$testFoo = new Foo($bar, $baz);

?>

如果有容器,那么 Dependency Injection 看起来就像:

代码语言:javascript
复制
<?php

// In our test, using PHPUnit's built-in mock support
$container = $this->getMock('Container');
$container['bar'] = $this->getMock('Bar');
$container['baz'] = $this->getMock('Baz');

$testFoo = new Foo($container['bar'], $container['baz']);

?>

通过引入容器,我们可以把所有的依赖都集中管理,这样有很多好处,比如说我们可以很方便的替换某种依赖的实现方式,从而提升系统的灵活性。

看看下面这个实现怎么样?是不是 Dependency Injection?

代码语言:javascript
复制
<?php

class Foo
{
    protected $_bar;
    protected $_baz;

    public function __construct(Container $container) {
        $this->_bar = $container['bar'];
        $this->_baz = $container['baz'];
    }
}

// In our test, using PHPUnit's built-in mock support
$container = $this->getMock('Container');
$container['bar'] = $this->getMock('Bar');
$container['baz'] = $this->getMock('Bar');

$testFoo = new Foo($container);

?>

虽然从表面上看它也使用了容器,并不依赖具体的实现,但你如果仔细看就会发现,它依赖了容器本身,实际上这不是 Dependency Injection,而是 Service Locator。

于是乎判断 Dependency Injection 和 Service Locator 区别的关键是在哪使用容器:

  • 如果在非工厂对象的外面使用容器,那么就属于 Dependency Injection。
  • 如果在非工厂对象的内部使用容器,那么就属于 Service Locator。

之所以排除工厂对象是因为它是一种特殊的对象,它关注的是创建对象,而不是操作对象,具体的解释可以参考 Paul M. Jones一系列文章中的解释

说到这里,我想顺带提一下 Laravel 的 Facade 概念,它是一种 Service Locator 的语法糖,原理可以参考:How Laravel Facades Work and How to Use Them Elsewhere。很多人建议 Stop Using Facades,Laravel 作者也给出了回应

BTW:Laravel 中的 Facade 实际有误导之嫌,详见:Let’s Talk About Facades

说了这么多,我们应该如何取舍 Dependency Injection 和 Service Locator 呢?实际上它们各有各的优缺点,比如说 Dependency Injection 解耦更彻底,而 Service Locator 使用更直接。如果是一些可复用性强的对象,如 Model,那么它的依赖最好使用 Dependency Injection 来获取;如果是一些可复用性弱的对象,如 Controller,那么它的依赖并不一定要强解耦,使用 Service Locator 来获取也不错,这样也更简单直接。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档