复杂的问题

| 选择喜欢的代码风格  

控制反转


顾名思义,一个系统通过组织控制和对象的完全分离来实现”控制反转”。对于依赖注入,这就意味着通过在系统的其他地方控制和实例化依赖对象,从而实现了解耦。

一些 PHP 框架很早以前就已经实现控制反转了,但是问题是,应该反转哪部分以及到什么程度?比如, MVC 框架通常会提供超类或者基本的控制器类以便其他控制器可以通过继承来获得相应的依赖。这就是控制反转的例子,但是这种方法是直接移除了依赖而不是减轻了依赖。

依赖注入允许我们通过按需注入的方式更加优雅地解决这个问题,完全不需要任何耦合。

依赖反转准则


依赖反转准则是面向对象设计准则 S.O.L.I.D 中的 “D” ,倡导 “依赖于抽象而不是具体”。简单来说就是依赖应该是接口/约定或者抽象类,而不是具体的实现。我们能很容易重构前面的例子,使之遵循这个准则

<?php
namespace Database;

class Database
{
    protected $adapter;

    public function __construct(AdapterInterface $adapter)
    {
        $this->adapter = $adapter;
    }
}

interface AdapterInterface {}

class MysqlAdapter implements AdapterInterface {}

现在 Database 类依赖于接口,相比依赖于具体实现有更多的优势。

假设你工作的团队中,一位同事负责设计适配器。在第一个例子中,我们需要等待适配器设计完之后才能单元测试。现在由于依赖是一个接口/约定,我们能轻松地模拟接口测试,因为我们知道同事会基于约定实现那个适配器

这种方法的一个更大的好处是代码扩展性变得更高。如果一年之后我们决定要迁移到一种不同的数据库,我们只需要写一个实现相应接口的适配器并且注入进去,由于适配器遵循接口的约定,我们不需要额外的重构。

容器


你应该明白的第一件事是依赖注入容器和依赖注入不是相同的概念。容器是帮助我们更方便地实现依赖注入的工具,但是他们通常被误用来实现反模式设计:服务定位(Service Location)。把一个依赖注入容器作为服务定位器( Service Locator)注入进类中隐式地建立了对于容器的依赖,而不是真正需要替换的依赖,而且还会让你的代码更不透明,最终变得更难测试。

大多数现代的框架都有自己的依赖注入容器,允许你通过配置将依赖绑定在一起。这实际上意味着你能写出和框架层同样干净、解耦的应用层代码。

如何理解 Laravel 中的依赖注入和容器这两个概念?


Service Container,当A类工作需要依赖B、C类时,传统会在A类内部进行new B,new C后才能使用B,C提供的服务,这样会使得A类与B,C严重耦合。但是有了Service Container,就可以把依赖的构造过程由容器去解决,而A类只需要在Constructor或Method Getter/Setter中注入B和C类或者B,C的抽象(接口),这就是构造器依赖注入和方法依赖注入(Dependency Injection),实现A类与B,C的解耦。如:如果注入的是B,C的接口,就可以随时替换掉B,C成为D,E,D,E只要实现对应的接口就行,这样A就与B,C实现松耦合。

Laravel中往Service Container 中绑定服务的就是三种方法:bind(), singleton(), instance();解析出服务的是make()方法。解决构造依赖或方法依赖的是 \\Illuminate\\Container\\Container::getDependencies(), \\Illuminate\\Container\\Container::call() 方法。当中一个重要知识点就是利用了 PHP 的 Reflection 特性。

PHP 控制反转、依赖注入、容器的延伸阅读:




发表评论