laravel ioc_Laravel IoC的依赖注入

laravel ioc

As developers, we are always trying to find new ways to write well designed and clean code by adopting new styles, using design patterns, and trying new robust frameworks. In this article we will explore the dependency injection design pattern through Laravel’s IoC component and see how it can improve our design.

作为开发人员,我们一直在尝试通过采用新样式,使用设计模式并尝试新的健壮框架来找到编写良好设计和简洁代码的新方法。 在本文中,我们将通过Laravel的IoC组件探索依赖注入设计模式,并了解它如何改善我们的设计。

依赖注入 (Dependency Injection)

Dependency injection is a term coined By Martin Fowler, and it’s the act of injecting components into your application. Like Ward Cunningham said:

依赖注入是Martin Fowler创造的一个术语,它是将组件注入到您的应用程序中的行为。 就像沃德·坎宁安(Ward Cunningham)所说:

Dependency Injection is a key element of agile architecture.

依赖注入是敏捷架构的关键要素。

let’s see an example:

让我们来看一个例子:

class UserProvider{
    protected $connection;
    
    public function __construct(){
        $this->connection = new Connection;
    }
    
    public function retrieveByCredentials( array $credentials ){
        $user = $this->connection
                        ->where( 'email', $credentials['email'])
                        ->where( 'password', $credentials['password'])
                        ->first();
                        
        return $user;
    }
}

If you want to test or maintain this class, you would have to access a real database and do some queries. To avoid having to do that and to decouple the class from the rest, you have one of three options to inject the Connection class without actually using it directly.

如果要测试或维护此类,则必须访问真实的数据库并进行一些查询。 为避免这样做,并使类与其余类脱钩 ,您可以使用三个选项之一来注入Connection类,而无需实际直接使用它。

When injecting components into your class you can use one of the three options:

将组件注入类时,可以使用以下三个选项之一:

构造函数注入 (Constructor Injection)

class UserProvider{
    protected $connection;
    
    public function __construct( Connection $con ){
        $this->connection = $con;
    }
    ...

二传手注射 (Setter Injection)

Similarly we can inject our dependency using a setter method:

同样,我们可以使用setter方法注入依赖项:

class UserProvider{
    protected $connection;
    public function __construct(){
        ...
    }
    
    public function setConnection( Connection $con ){
        $this->connection = $con;
    }
    ...

接口注入 (Interface Injection)

interface ConnectionInjector{
    public function injectConnection( Connection $con );
}

class UserProvider implements ConnectionInjector{
    protected $connection;
    
    public function __construct(){
        ...
    }
    
    public function injectConnection( Connection $con ){
        $this->connection = $con;
    }
}

When a class implements our interface, we define the injectConnection method to resolve the dependency.

当类实现我们的接口时,我们定义injectConnection方法来解决依赖关系。

优点 (Advantages)

Now, when testing our class we can mock the dependency class and pass it as a parameter. Each class must be focused on a particular task and should not be concerned with resolving their dependencies. That way, you will have a better focused and maintainable application.

现在,在测试我们的类时,我们可以模拟依赖项类并将其作为参数传递。 每个类都必须专注于特定任务,并且不应该关注解决其依赖关系。 这样,您将拥有一个更加集中和可维护的应用程序。

If you’d like to know more about DI, Alejandro Gervassio covered it extensively and professionally in this series, so be sure to give these articles a read. Now, what about IoC? Ioc (Inversion of control) is not necessary to use dependency injection, but it can help you to manage your dependencies effectively.

如果您想了解有关DI的更多信息,Alejandro Gervassio在本系列对此进行了广泛而专业的介绍,因此请务必阅读这些文章。 现在,IoC呢? Ioc(控制反转)对于使用依赖项注入不是必需的,但它可以帮助您有效地管理依赖项。

控制反转 (Inversion Of Control)

Ioc is a simple component that makes resolving dependencies more convenient. You describe your objects to the container, and every time you resolve a class, the dependencies are injected automatically.

Ioc是一个简单的组件,使解析依赖项更加方便。 您向容器描述对象,并​​且每次您解析类时,都将自动注入依赖项。

Laravel Ioc (Laravel Ioc)

Laravel Ioc is somehow special with its way of resolving dependencies, when you ask for an object:

Laravel Ioc在您请求对象时以某种方式解决依赖关系的方式很特别:

alt

We will use a simple example that we will improve during this article. The SimpleAuth class has a dependency of FileSessionStorage, so our code might look like this:

我们将使用一个简单的示例,在本文中我们将对其进行改进。 该SimpleAuth类具有的依赖FileSessionStorage ,所以我们的代码可能是这样的:

class FileSessionStorage{
  public function __construct(){
    session_start();
  }
  
  public function get( $key ){
    return $_SESSION[$key];
  }

  public function set( $key, $value ){
    $_SESSION[$key] = $value;
  }
}

class SimpleAuth{
  protected $session;

  public function __construct(){
    $this->session = new FileSessionStorage;
  }
}

//creating a SimpleAuth
$auth = new SimpleAuth();

This is the classic way of doing it, let’s start by using the constructor injection.

这是经典的方法,让我们从构造函数注入开始。

class SimpleAuth{
  protected $session;

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

Now we create our object:

现在我们创建对象:

$auth = new SimpleAuth( new FileSessionStorage() );

Now I want to use Laravel Ioc to manage all of that.

现在,我想使用Laravel Ioc来管理所有这些。

Because the Application class extends the Container class, you can always access the container via the App facade.

由于Application类扩展了Container类,因此您始终可以通过App Facade访问容器。

App::bind( 'FileSessionStorage', function(){
    return new FileSessionStorage;
});

The first parameter for the bind method is a unique id to bind to the container, the second is a callback function to be executed each time we resolve the FileSessionStorage class, we can also pass a string representing class name as we will see next.

bind方法的第一个参数是绑定到容器的唯一ID,第二个参数是每次我们解析FileSessionStorage类时要执行的回调函数,我们还可以传递表示类名的字符串,如下所示。

Note: if you inspect Laravel packages you will that sometimes bindings are grouped like ( view, view.finder..).

注意:如果您检查Laravel软件包,有时绑定将被分组为( viewview.finder ..)。

Let’s say that maybe we want to switch our session storage to MySql, our class should be similar to:

假设我们想将会话存储切换到MySql,我们的类应该类似于:

class MysqlSessionStorage{

  public function __construct(){
    //...
  }

  public function get($key){
    // do something
  }

  public function set( $key, $value ){
    // do something
  }
}

Now that we have changed the dependency, we need to change the SimpleAuth constructor and bind a new object to the container!

现在我们已经更改了依赖关系,我们需要更改SimpleAuth构造函数并将新对象绑定到容器!

High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

高级模块不应依赖于低级模块。 两者都应依赖抽象。 抽象不应依赖细节。 细节应取决于抽象。

Robert C. Martin

罗伯特·马丁

Our SimpleAuth class should not be concerned about how our storage is done, instead it should focus more on just consuming the service.

我们的SimpleAuth类不应关注存储的完成方式,而应仅关注使用服务。

So we can abstract our storage implementation:

因此,我们可以抽象化存储实现:

interface SessionStorage{
  public function get( $key );
  public function set( $key, $value );
}

This way we can just implement and ask for an instance of the SessionStorage interface:

这样,我们就可以实现并请求SessionStorage接口的实例:

class FileSessionStorage implements SessionStorage{

  public function __construct(){
    //...
  }

  public function get( $key ){
    //...
  }

  public function set( $key, $value ){
    //...
  }
}

class MysqlSessionStorage implements SessionStorage{

  public function __construct(){
    //...
  }

  public function get( $key ){
    //...
  }

  public function set( $key, $value ){
    //...
  }
}

class SimpleAuth{

  protected $session;

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

}

If we try to resolve the SimpleAuth class through the container using App::make('SimpleAuth'), the container will throw a BindingResolutionException, after trying to resolve the class from the bindings, falling back to the reflection method and resolving all the dependencies.

如果我们尝试使用App::make('SimpleAuth')通过容器来解析SimpleAuth类,则在尝试从绑定中解析类之后,容器将抛出BindingResolutionExceptionBindingResolutionException反射方法并解决了所有依赖关系。

Uncaught exception 'Illuminate\Container\BindingResolutionException' with message 'Target [SessionStorage] is not instantiable.'

The container is trying to instantiate the interface. We can fix that by creating a specific binding for our interface.

容器正在尝试实例化接口。 我们可以通过为接口创建特定的绑定来解决此问题。

App:bind( 'SessionStorage', 'MysqlSessionStorage' );

Now every time we try to resolve the interface through the container, we will get a MysqlSessionStorage instance. If we want to switch our storage service we can just update the bindings.

现在,每次我们尝试通过容器解析接口时,我们都会获得一个MysqlSessionStorage实例。 如果我们要切换存储服务,我们可以更新绑定。

Note: if you want to see if a class is bound to the container you can use App::bound('ClassName') or use the App::bindIf('ClassName') to register a binding if it hasn’t already been registered.

注意:如果要查看是否将类绑定到容器,则可以使用App::bound('ClassName')或使用App::bindIf('ClassName')注册绑定(如果尚未绑定App::bindIf('ClassName')注册。

Laravel Ioc also offers App::singleton('ClassName', 'resolver') for shared bindings. You can also use App::instance('ClassName', 'instance') to create a shared instance. If the container can’t resolve the dependency it will throw a ReflectionException, but we can use the App::resolvingAny(Closure) to resolve any given type or as a form of fallback.

Laravel Ioc还提供App::singleton('ClassName', 'resolver')用于共享绑定。 您也可以使用App::instance('ClassName', 'instance')创建共享实例。 如果容器无法解析依赖关系,它将抛出ReflectionException ,但是我们可以使用App::resolvingAny(Closure)解析任何给定的类型或后备形式。

Note: if you register a resolver for a given type, the resolvingAny method will be also called, but the value from the bind method is returned.

注意:如果注册给定类型的解析器,也会调用resolvingAny方法,但是将返回bind方法的值。

最后提示 (Final Tips)

  • Where to store bindings:

    绑定存储位置:

    If you have just a small application you can use your

    如果您只有一个小型应用程序,则可以使用

    global/start.php, but if your project is getting larger you must use a service provider.

    global/start.php ,但是如果您的项目越来越大,则必须使用服务提供商

  • Testing:

    测试:

    When you’re just testing you need to consider using

    在进行测试时,您需要考虑使用

    php artisan tinker, it’s very powerful, and can increase your Laravel testing workflow.

    php artisan tinker ,它非常强大,并且可以增加您的Laravel测试工作流程。

  • Reflection API:

    反射API:

    The PHP Reflection API is very powerful and if you want to understand the Laravel Ioc you need to get familiar with the Reflection API, be sure to check this

    PHP Reflection API非常强大,如果您想了解Laravel Ioc,则需要熟悉Reflection API,请务必进行检查

    tutorial for more information.

    教程以获取更多信息。

结论 (Conclusion)

As always, the best way to learn about something is inspecting the source code. Laravel Ioc is just one file and shouldn’t take you long to go through all the functions. Would you like to know more about Laravel IoC or IoC in general? Let us know!

与往常一样,了解某事的最好方法是检查源代码。 Laravel Ioc只是一个文件,不需要花费很长时间即可完成所有功能。 您是否想进一步了解Laravel IoC或IoC? 让我们知道!

翻译自: https://www.sitepoint.com/dependency-injection-laravels-ioc/

laravel ioc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值