服务定位器,依赖注入(和容器)以及控制反转

12

我已经编程有一段时间了,但从未对理论知识感兴趣,可能会使用各种编程概念,但并不知道它的含义。

服务定位器: 对我来说,服务定位器是指记录快捷方式以通过减少代码量加速开发的工具。一个问题是:定位器是否只能指命名空间/类,还是我可以拥有变量的注册表?

这是我的理解:

$locator = new ServiceLocator()
$locator->set('app', new System\Application());
$locator->set('db', new System\Pdo());

// Get the objects
$locator->get('db')->connect();
$locator->get('app')->run();

依赖注入(Dependency Injection)和依赖注入容器(Dependency Injection Container):

在对象内部注入其他对象,无论是否使用工厂模式都可以更快地访问这些对象。那么什么是DI容器呢?

以下是我的理解:

$app = new System\Application(System\Config::load());

控制反转(Inversion of Control):

不理解这个设计模式(或者理解了但不确定我所做的是否属于IoC)。

那么,从理论上讲(最好有简单的例子),每个概念都意味着什么?我的理解正确吗,还有哪些错误/可以改进的地方?

谢谢!


不确定您所说的“更快的访问”是什么意思。全局变量或单例模式同样快。我认为您所说的更多是关于模块化的问题。 - landons
当我有一个 DI 时,整个类被注入到另一个类中,因此,我不需要另一个设计模式,因为我在我的第二个类中拥有所有所需的内容。(明白吗?!) - Gabriel Santos
等一下.. 谁在这里提问? :P - landons
我把你的评论理解成了一个问题(巴西初学者的事情)。你所说的模块化是什么意思?DI、IoC和服务定位器与此有关系吗?能否再详细解释一下? - Gabriel Santos
1
将注入视为一种松耦合形式。不同的模块不必“知道”如何访问彼此,而是通过注入引用。这使得更改不同的模块变得显着容易,而不会破坏您遗忘的内容。这不仅是便捷的访问方式,还是可持续代码的模块化方法。 - landons
2个回答

20

服务定位和依赖注入首先是为了解耦类,以便它们可以轻松地进行测试和更改。

当您将IoC容器的注册解析部分与服务定位器进行比较时,它似乎是相同的。

您可以将IoC容器用作服务定位器,但这被认为是一种反模式。当您使用服务定位器时,您始终需要在整个架构中主动调用服务定位器。因此,您解耦了类,但另一方面,您将它们全部耦合到服务定位器中。此外,使用服务定位器更难发现依赖关系,因为您正在隐藏依赖项。而使用依赖注入,通过使用构造函数注入,可以使依赖关系“公开”。

当您使用IoC容器时,您使用依赖注入(构造函数注入或属性注入)。现在,IoC容器能够通过查看构造函数参数并创建整个依赖关系图来解决依赖关系图。这被称为 自动装配。服务定位器无法自动连接依赖项。正如我之前提到的,您不必使用自动装配,您可以通过直接在每个类中调用IoC容器来轻松使用IoC容器,但是您不应该这样做

另请参见:https://dev59.com/DOo6XIcBkEYKwwoYPSL7#11319026

enter image description here


我完全不了解IoC。你能提供一些示例吗? - Gabriel Santos
@GabrielSantos 我只能发布.NET的示例。 - Rookian
谢谢@Rookian,但是不懂.NET =P - Gabriel Santos
说实话,我有类似的问题 - 我使用了服务定位器(反模式),我认为我理解了依赖注入,但从我的角度来看,控制反转则是依赖注入或服务定位器 - 这取决于它的使用方式。在我看来,它是由可能的实现组成的IoC组:服务定位器或依赖注入...虽然我是PHP开发人员,但我做过一点.NET(C#),如果您仍然愿意提供.NET IoC的示例,我想我可以使用您的示例...谢谢!;-) - shadyyx
@shadyyx 没错。有点令人困惑,因为人们习惯称之为 DI 容器或 IoC 容器。该框架是一个 IoC 容器,通常用作 DI 工具。个人倾向于称其为 DI 容器 :) - Rookian

10

我认为你正确地理解了服务定位器。

关于依赖注入,这意味着如果一个对象具有构造函数和/或属性依赖关系,则这些依赖关系将由外部注入到对象中,而不是对象自己获取这些依赖关系。

public class MyClass
{
   private $_dep;
   public function __construct($dep=null)
   {
       //$dep has to be injected
       $this->_dep=$dep;                           
   }

   //this is wrong because it couples MyClass to a specific Dependency implementation
   public function __construct()
   {
       $this->_dep=new Dependency();
    }
}
   $dep=new Dependency();
   $obj=new MyClass($dep);

通常构造函数需要一个抽象 (即接口) 作为参数,在类外实例化一个具体实现,然后在创建 MyClass 的新实例时将其传递给构造函数。

依赖注入容器可以自动处理依赖注入。您只需要配置它,让它知道在请求抽象时返回哪些具体类。容器会处理对象的创建、通过构造函数和/或属性注入依赖项。根据容器的不同(我不知道 php 的例子,我只熟悉 .net DI 容器),你可能还需要注册容器可以创建的对象的类型。

控制反转 意味着较高级别的类不再依赖于较低级别的类 (依赖),而是反过来: 较低级别的类实现依赖于较高级别的类所需的抽象。

//abstraction defined for the use of higher level class
public interface  IRepository {}

// that's the dependency, the lower level class  
public class XmlRepository implements IRepository {}

//the higher level class
 public class MyClass
 {
     public function __construct(IRepository $repo) {}
  }

IoC和DiC是一同使用的,因为DI容器提供了IoC功能。


很好的解释。那么,DI 是指注入一个对象并将其存储在您注入的类内部,以允许将来访问吗? - Gabriel Santos
关于IoC,我不是很理解,如果您能够更详细地解释一下,我将不胜感激。 - Gabriel Santos
关于 DI,是的。关于 IoC,我不知道如何更好地解释它,但每当一个类指定它需要一个抽象作为依赖项时,那就是 IoC 模式。 - MikeSW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接