php - Laravel的容器绑定(bind)机制有何不同?

标签 php laravel design-patterns ioc-container

我在看拉维的service container docs,特别是绑定部分。
有什么不同,什么时候应该使用每种类型的绑定?文件提到:
简单绑定
单子绑定
实例绑定
基元绑定
接口绑定

最佳答案

首先,让我们看看它到底是什么:
ioc容器是一个知道如何创建实例的组件。它还知道它们的所有底层依赖项以及如何解决它们。
容器关于实例创建和依赖关系解析的知识可能由程序员指导。这就是为什么laravel的容器为我和您提供了各种绑定api。
“解决容器外问题”是一个你经常读/听到的短语。这意味着你告诉容器根据你之前给她的[可选]指导为你做一些东西。
在继续阅读有关绑定的内容之前,我强烈建议您阅读以下答案:
What is Laravel IoC Container in simple words?
简单绑定

app()->bind(DatabaseWriter::class, function ($app) {
    return new DatabaseWriter(
        $app->make(DatabaseAdapterInterface)
    );
});

你对容器说,当你想解析一个DatabaseWriter类的实例时,遵循我在闭包中告诉你的逻辑,因为我知道得更好。每次您想要解析类时,都必须遵循此操作并为我传递一个新实例。
您始终使用这种类型的绑定。你在给容器一些小食谱,告诉它如何为你制作你的东西。
单子绑定
与简单绑定相同,但有一个明显的区别。你告诉容器我只需要在整个应用程序中使用这个类的一个实例。第一次解析类时,请遵循我传递给您的闭包中的逻辑,但请确保每隔一次您想解析它时只返回一个实例。把你被允许做的唯一一个例子给我。
这是单身汉,对吧?解析单例绑定后,在随后调用容器时将返回相同的对象实例。
显然,当您想使用singleton模式时,可以使用这种类型的绑定。现在很少见。
实例绑定
这就像帮集装箱的忙。你不告诉她如何实例化某个类,你自己做,只给她实例。她帮你拿着它,然后在随后的调用中返回到容器中。
在进行单元测试时,它特别方便。如果将一个模拟实例绑定到某个类的容器,那么对app()->make()的所有后续调用都将为您返回该模拟。所以当使用实际的类时,实际上是在整个应用程序中注入一个类模拟。
class QuestionsControllerTest extends TestCase 
{  
    public function testQuestionListing()
    {
        $questionMock = Mockery::mock('Question')
           ->shouldReceive('latest')
           ->once()
           ->getMock();

        // You're telling the container that everytime anyone
        // wants a Question instance, give them this mock I just
        // gave you.
        $this->app->instance('Question', $this->questionMock);

        // whatever...
    }
}

基元绑定
laravel的容器提供了一个dsl来告诉她如何解析原语。你说当BillingController类想要一个$taxRate变量而它没有被传递时,给它0.2。这就像是在遥远的地方设置默认值!
app()->when('App\Http\Controllers\BillingController')
     ->needs('$taxRate')
     ->give(.2);

用例可能很少见,但您可能偶尔需要它们。这个例子可能更感性一点:
app()->when('App\Http\Controllers\CustomerController')
    ->needs('$customers')
    ->give(function() {
        return Customer::paying();
    });

接口绑定
当您希望将接口绑定到具体实现时,就会使用它。
在阅读了十几篇关于solid和如何成为一个更好的程序员的文章之后,您决定遵循依赖性反转原则,而不是依赖于具体的实例,而是依赖于抽象。
毕竟,这是一个很好的做法,无论是在拉沃尔还是在拉沃尔。
class DatabaseWriter {

    protected $db;

    // Any concrete implementation of this interface will do
    // Now, that I depend on this DatabaseAdapterInterface contract,
    // I can work with MySQL, MongoDB and WhatevaDB! Awesome!
    public function __construct(DatabaseAdapterInterface $db)
    {
        $this->db = $db;
    }

    public function write()
    {
        $this->db->query('...');
    }

}

如果没有laravel的容器,首先需要创建DatabaseAdapterInterface的具体实现,并将其传递给DatabaseWriter的构造函数,以便能够实例化它:
$dbWriter = new DatabaseWriter(new MongodbAdapter)

如果MongodbAdapter有自己的依赖项,您可能会在这里结束:
// Looks familiar, right? 
// These are those recipes you used to give to Laravel container 
// through simple binding. 
$dbWriter = new DatabaseWriter(new MongodbAdapter(new MongodbConnection))

但是在派对上有了拉维尔的容器,你告诉她,当有人要求具体实现DatabaseAdapterInterface时,不要再问了,给他们一个MongodbAdapter
app()->bind(DatabaseAdapterInterface::class, MongodbAdapter::class)

然后您继续解决容器外DatabaseWriter的一个实例,就像一个boss:
$dbWriter = app()->make(DatabaseWriter::class)

更简单更干净,对吧?你把所有明显的杂物都移到别的地方。你的可能。
好吧,让我们看看她在这个场景中是如何工作的。首先,她探测AppServiceProvider可能的依赖关系(通过reflection),发现它需要一个DatabaseWriter。检查她的笔记本,回忆起你告诉她DatabaseAdapterInterface是该接口的具体实现。制作一个并交给MongodbAdapter
如果您坚持依赖项反转原则,那么您几乎一直在使用这些类型的绑定。
好了,够了,让我们看看她到底是怎么工作的:
https://github.com/laravel/framework/blob/5.3/src/Illuminate/Container/Container.php#L627

关于php - Laravel的容器绑定(bind)机制有何不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40767040/

相关文章:

python - 如何设计插件架构,其中插件填充网页的不同部分?

design-patterns - 关于设计模式: When should I use the singleton?

php - 搜索只读到一个关键字 : SQLSTATE[HY093]: Invalid parameter number

javascript - PHP ECHO 到 JSON 结果 = JSON.parse : expected property name or '}' at line 1 column 2 of the JSON data

php - 在长时间请求后添加消息时 flashMessenger 不工作

PHP:跟踪用户是否在线的最有效方式?

laravel - Laravel 中允许的内存大小为 536870912 字节

php - Laravel闭包,什么是$query,$query是如何传递的?

Laravel 唯一和存在验证 - 类不存在

c# - 在公共(public) API 中使用可空类型