The IoC Container 控制反转容器

The IoC Container 控制反转容器

Basic Binding 基础绑定

Now that we’ve learned about dependency injection, let’s explore inversion of control containers. IoC containers make managing your class dependencies much more convenient, and Laravel ships with a very powerful container. The IoC container is the certral piece of the Laravel framework, and it is what allows all of the framework’s jcomponents jto work together. In fact, the Laravel Application class extends the Container class!

我们已经学习了依赖注入,接下来咱们一起来探索“控制反转容器”(IoC)。 IoC容器可以使你更容易管理依赖注入,Laravel框架拥有一个很强大的IoC容器。Laravel的核心就是这个IoC容器,这个IoC容器使得框架各个组件能很好的在一起工作。事实上Laravel的Application类就是继承自Container类!

IoC Container 控制反转容器

Inversion of control containers make dependency injection more convenient. How to resolve a given class or interface is defined once in the container, which manages resolving and injecting those objects throughout your application.

控制反转容器使得依赖注入更方便。当一个类或接口在容器里定义以后,如何处理它们——如何在应用中管理、注入这些对象?

In a Laravel application, the IoC container can be accessed via the App facade. The container has a variety of methods, but we’ll start with the most basic. Let’s continue to use our BillerInterface and BillingNotifierInterfacefrom the previous chapter, and assume that our application is using Stripe to process payments. We can bind the Stripe implementation of the interface to the container like this:

在Laravel应用里,你可以通过App来访问控制反转容器。容器有很多方法,不过我们从最基础的开始。让我们继续使用上一章写的BillerInterface和BillingNotifierInterface,且假设我们使用了Stripe来进行支付操作。我们可以将Stripe的支付实现绑定到容器里,就像这样:

<!-- lang: php -->
App::bind('BillerInterface', function()
{
    return new StripeBiller(App::make('BillingNotifierInterface'));
});

Notice that within our BillingInterface resolver, we also resolve a BillingNotifierInterface implementation. Let’s define that binding as well:

注意在我们处理BillingInterface时,我们额外需要一个BillingNotifierInterface的实现,也就是再来一个bind:

<!-- lang: php -->
App::bind('BillingNotifierInterface', function()
{
    return new EmailBillingNotifier;
});

So, as you can see, the container is a place to store Closures that resolve various classes. Once a class has been registered with the container, we can easily resolve it from anywhere in our application. We can even resolve other container bindings within a resolver.

如你所见, 这个容器就是个用来存储各种绑定的地方(译者注:这么理解简单点。再扯匿名函数、闭包就扯远了。)。一旦一个类在容器里绑定了以后,我们可以很容易的在应用的任何位置调用它。我们甚至可以在bind函数内写另外的bind。

Have Acne?

The Laravel IoC container is a drop-in replacement for the Pimple IoC container by Fabien Potencier. So, if you’re already using Pimple on a project, feel free to upgrade to the Illuminate Container component for a few more features!

Laravel框架的Illuminate容器和另一个名为Pimple的IoC容器是可替换的。所以如果你之前用的是Pimple,你尽可以大胆的升级为Illuminate Container,后者还有更多新功能!

Once we’re using the container, we can switch interface implementations with a single line change. For example, consider the following:

一旦我们使用了容器,切换接口的实现就是一行代码的事儿。 比如考虑以下代码:

<!-- lang: php -->
class UserController extends BaseController{

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

When this controller is instantiated via the IoC container, the StripeBiller, which includes the EmailBillingNotifier, will be injected into the instance. Now, if we want to change our notifier implementation, we can simply change the binding to this:

当这个控制器通被容器实例化后,包含着EmailBillingNotifier的StripeBiller会被注入到这个控制器中(译者注:见上文的两个bind)。如果我们现在想要换一种提示方式,我们可以简单的将代码改为这样:

<!-- lang: php -->
App::bind('BillingNotifierInterface', function()
{
    return new SmsBillingNotifier;
});

Now, it doesn’t matter where the notifier is resolved in our application, we will now always get the SmsBillingNotifier implementation. Utilizing this architecture, our application can be rapidly shifted to new implementations of various services.

现在不管在应用的哪里需要一个提示器,我们总会得到SmsBillingNotifier的对象。利用这种结构,我们的应用可以在不同的实现方式之间快速切换。

Being able to change implementations of an interface with a single line is amazingly powerful. For example, imagine we want to change our SMS service from a legacy provider to Twilio. We can develop a new Twilio implementation of the notifier and swap our binding. If we have problems with the transition to Twilio, we can quickly change back to the legacy provider by making a single IoC binding change. As you can see, the benefits of using dependency injection go beyond what is immediately obvious. Can you think of more benefits for using dependency injection and an IoC container?

只改一行就能切换代码实现,这可是很厉害的能力。比如我们想把短信服务从原来的提供商替换为Twilio。我们可以开发一个新的Twilio的提示器类(译者注:当然要继承自BillingNotifierInterface)然后修改绑定语句。如果Twilio有任何闪失,我们只需修改一行代码就可以快速的切换回原来的短信提供商。看到了吧,依赖注入的好处多得很呢。你能再想出几个使用依赖注入和控制反转容器的好处么?

Sometimes you may wish to resolve only one instance of a given class throughout your entire application. This can be achieved via the singleton method on the container class:

想在应用中只实例化某类一次?没问题,使用singleton方法吧:

<!-- lang: php -->
App::singleton('BillingNotifierInterface', function()
{
    return new SmsBillingNotifier;
});

Now, once the container has resolved the billing notifier once, it will continue to use that same instance for all subsequent requests for that interface.

这样只要这个容器生成了这个提示器对象一次, 在接下来的生成请求中容器都只会提供这同样的一个对象。

The instance method on the container is similar to singleton, however, you are able to pass an already existing object instance. The instance you give to the container will be used each time the container needs an instance of that class:

容器的instance方法和singleton方法很类似,区别是instance可以绑定一个已经存在的对象。然后容器每次返回的都是这个对象了。

<!-- lang: php -->
$notifier = new SmsBillingNotifier;
App::instance('BillingNotifierInterface', $notifier);

Now that we’re familiar with basic container resolution using Closures, let’s dig into its most powerful feature: the ability to resolve class via reflection.

现在我们熟悉了容器的基础用法,让我们深入发掘它更强大的功能:依靠反射来处理类和接口。

Stand Alone Container容器独立运行

Working on a project that isn’t built on Laravel? You can still utilize Laravel’s IoC container by installing the illuminate/container package via Composer!

你的项目没有使用Laravel?但你依然可以使用Laravel的IoC容器!只要用Composer安装了illuminate/container包就可以了。

Reflect Resolution 反射解决方案

One of the most powerful features of the Laravel container is its ability to automatically resolve dependencies via reflection. Reflection is the ability to inspect a classes and methods. For example, the PHP ReflectionClass class allows you to inspect the method avaliable on a given class. The PHP method method_exists is also a form of reflection. To play with PHP’s reflection class, try the followring code on one of your classes:

用反射来自动处理依赖是Laravel容器的一个最强大的特性。反射是一种运行时探测类和方法的能力。比如,PHP的ReflectionClass可以探测一个类的方法。method_exists某种意义上说也是一种反射。我们来把玩一下PHP的反射类,试试下面的代码吧(StripeBiller换成你自己定义好的类):

<!-- lang: php -->
$reflection = new ReflectionClass('StripeBiller');
var_dump($reflection->getMethods());
var_dump($reflection->getConstants());

By leveraging this powerful feature of PHP, the Laravel IoC container can do some interesting things! For instance, consider the following class:

依靠这个强大的PHP特性,
Laravel的IoC容器可以实现很有趣的功能!考虑接下来这个类:

<!-- lang:php -->
class UserController extends BaseController
{
    public function __construct(StripBiller $biller)
    {
        $this->biller = $biller;
    }
}

Note that the controller is type-hinting the StripBiller class. We are able to retrieve this type-hint using reflection. When the Laravel container does not have a resolver for a class explictity bound, it will try to resolve the class via reflection. The flow looks like this:

注意这个控制器的构造函数暗示着有一个StripBiller类型的参数。使用反射就可以检测到这种类型暗示。当Laravel的容器无法解决一个类型的明显绑定时,容器会试着使用反射来解决。程序流程类似于这样的:

    1. Do I have a resolver for StripBiller?
      已经有一个StripBiller的绑定了么?
    1. No resolver? Reflect into StripBiller to determin if it has dependencies.
      没绑定?那用反射来探测一下StripBiller吧。看看他都需要什么依赖。
    1. Resolve any dependencies needed by StripBiller (recursive).
      解决StripBiller需要的所有依赖(递归处理)
    1. Instantiate new StripBiller instance via ReflectionClass->newInstanceArgs().
      使用ReflectionClass->newInstanceArgs()来实例化StripBiller

As you can see, the container is doing a lot of heavy lifting for you, which saves you from having to write resolves for every single one of your classes. This is one of the most powerful and unique features of the Laravel container, and having a strong grasp of this capability is very beneficial when building large Laravel applications.

如你所见, 容器替我们做了好多重活,这能帮你省去写大量绑定的麻烦。这就是Laravel容器最强大也是最独特的特性。熟练掌握这种能力对构建大型Laravel应用是十分有益的。

Now, let’s modify our controller a bit. What if it looks like this?

下面我们修改一下控制器, 改成这样会发生什么事儿呢?

<!-- lang:php -->
class UserController extends BaseController
{
    public function __construct(BillerInterface $biller)
    {
        $this->biller = $biller;
    }
}

Assuming we have not explicitly bound a resolver for BillerInterface, how will the container know what class to inject? Remember, interface can’t be instantiated since they are just contracts. Without us giving the container any more information, it will be unable to instantiate this dependency. We need to specify a class that should be used as the default implementation of this interface, and we may do so via the bind method:

假设我们没有为BillerInterface做任何绑定, 容器该怎么知道要注入什么类呢?要知道,interface不能被实例化,因为它只是个约定。如果我们不提供更多信息的话,容器是无法实例化这个依赖的。我们需要明确指出哪个类要实现这个接口,这就需要用到bind方法:

<!-- lang:php -->
App::bind('BillerInterface','StripBiller');

Here, we are passing a string instead of a Closure, and this string tells the container to always use the StripBiller class anytime it needs an implementation of the BillerInterface interface. Again, we’re gaining the ability to switch implementations of services with a simple one-line change to our container binding. For example, if we need to switch to Balanced Payments as our billing provider, we simply write a new BalancedBillerimplementation of BillerInterface, and change our container binding:

这里我们只传了一个字符串进去,而不是一个匿名函数。 这个字符串告诉容器总是使用StripBiller来作为BillerInterface的实现类。 此外我们也获得了只改一行代码即可轻松改变实现的能力。比如,假设我们需要切换到Balanced Payments作为我们的支付提供商,我们只需要新写一个BalancedBiller来实现BillerInterface接口,然后这样修改容器代码:

<!-- lang:php -->
App::bind('BillerInterface', 'BalancedBiller');

Automatically, our new implementation will be used throughout out application!

我们的应用程序就装载上了的新支付实现代码了!

When binding implementations to interfaces, you may also use the singleton method so the container only instantiates one instance of the class per request cycle:

你也可以使用singleton方法来实现单例模式。

<!-- lang:php -->
App::singleton('BillerInterface', 'StripBiller');
Master The Container 掌握容器

Want to learn even more about the container? Read through its source! The container is only one class: Illuminate\Container\Container. Read over the source code to gain a deeper understanding of how the container works under the hood.

想了解更多关于容器的知识? 去读源码!容器只有一个类Illuminate\Container\Container. 读完了你就对容器有更深的认识了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值