依赖注入防止依赖于依赖容器


Dependency Injection prevent dependency on the dependency container

在对依赖注入感到困惑之后,我担心会依赖于DI容器。所以我想把一切都和工厂联系在一起。但是似乎需要的工厂太多了(每个物品需要一个工厂)。

现在发生的事情如下:

class houseFactory
{
    protected $_di;
    public function __construct('DI $di)
    {
        $this->_setDI($di)
    }
    protected function _setDI('DI $di)
    {
        $this->_di = $di;
    }
    public function createObject()
    {
        return $this->_di->create('house');
    }
}

houseFactory基本上将创建和调用代码连接在一起,而不必知道如何创建房屋。但是,为每个需要的对象都建立一个新工厂真的有必要吗?

现在看来是这样,因为房子不是那么容易由DI容器单独解决的,而是需要一定的规则。例如,它应该只有一个实例(也称为"共享")。这些规则将进入工厂。

我的问题是:

1)可以接受吗?

我预计会有大量的工厂,我担心这将是一个过度的/巨大的缺点/不利于它应该做的事情。

2)什么时候使用"new"关键字是"ok"的?

我理解为什么移动逻辑来创建一个具有依赖关系的新对象是一种好做法。但它似乎是循环的。

3)依赖注入容器在哪里以及如何在应用程序中发挥作用?

从什么角度使用/调用DI容器?看起来像是将DI容器硬编码到应用程序中?或者这就是问题的关键?

希望我的问题不会太模糊。

DI的主要目的是注入依赖项,而不是注入负责解析/创建对象的对象。在您的示例中,假设我们想使用houseFactory类来获取house类并使用它(例如goHome方法)。

$house = $houseFactory->createObject();
$house->goHome();

实现不仅使你的调用依赖于house类,但它也依赖于houseFactory类,这是一个无用的依赖。

正如TrueWill所说,应用程序唯一负责创建对象的地方是composition rootcomposition root是每个应用程序的起点。不幸的是,我不认为在纯php类中有任何组合根,但是在php框架中应该有,如CI或Zend。

为什么要在composition root中创建对象?

  1. 使对象创建的范围变得非常小。组合根通常只包含启动应用程序的小逻辑。也许有一些其他的逻辑来处理一般的异常,而不是其他的。因此,创建对象是安全的,因为没有特定的业务逻辑存在。
  2. 没有在其他类中创建组合根。它在应用程序启动时被调用,所以在那里的类中创建对象是安全的。

要了解更多关于作文根,你可以阅读Mark Seeman的书或他的博客。

那么我们如何访问在组合根中创建的对象呢?

一些应用程序对DI有很好的支持,比如ASP。asp.net MVC,但是有些不像ASP。净Webform。在具有良好支持的应用程序中,您可以直接从composition root注入该类。但是,如果没有,也许您可以通过静态类访问复合根DI容器,但只能在页面的构造函数中访问。所以它最小化了对组合根的访问。

EDIT1:

这是用php编写的没有DI容器的house类的使用示例(可能不工作,没有在php中使用DI)。

class houseConsumer{
    protected $_house;
    public function __construct('DI $house)
    {
        $this->_house($house)
    }
    public function doSomething(){
        // do something
        $this->_house->goHome();
    }
}
class compositionRoot{
    public function main(){
        $diContainer = // define the DI Container
        $houseConsumer = new houseConsumer($diContainer->create('house'));
        $houseConsumer->doSomething();
    }
}

这是c#控制台的例子:

public class HouseConsumer{
    //constructor
    public HouseConsumer(IHouse house){
        this.house = house;
    }
    IHouse house;
    public void DoSomething(){
        house.GoHome();
    }
}
//this is the composition root
public class Program{ 
    public static void Main(string[] args){
        List<object> diContainer = new List<object>();
        //populate diComponent
        HouseConsumer consumer = new HouseConsumer((IHouse)diContainer[0]);
        consumer.DoSomething();
    }
}
编辑2:

DI容器实际上是在'main'方法之外使用的吗?如何在"main"方法之外创建新对象?假设我有几个对象深度。我怎么在那里建房子?

我上面的例子可以在Asp中使用。Net MVC,其中页面也使用IOC容器自动解析。不幸的是,如果您在Asp. js等页面中需要DI容器。Net webforms中,您应该使用static,但只能在构造函数中分配对象。示例(在c#中,我不知道如何在php中使用静态)。

public class Page1 : Page{
    public Page1(){
        this.house = CompositionRoot.DiContainer.Create("house");
    }
    private IHouse house;
}

这个实现仍然是安全的,因为diccontainer只在页面的构造函数级别使用。但是确保不要在服务类中这样做。例子:

public class Renter : IRenter{
    public Renter(){
        this.house = CompositionRoot.DiContainer.Create("house");
    }
    private IHouse house;
}

是一个糟糕的设计。Renter类依赖于diccontainer。为了解决这个问题,注入IHouse类。

public class Renter : IRenter{
    public Renter(IHouse house){
        this.house = house;
    }
    private IHouse house;
}

那么Renter类的调用者应该通过house类。示例是使用poor man's.

将IRenter类赋值给diccontainer。
Dictionary<Type, object> DiContainer = new Dictionary<Type, object>();
IHouse house = new House();
IRenter renter = new Renter(house);
DiContainer.Add(typeof(IHouse), house);
DiContainer.Add(typeof(IRenter), renter);

然后在Page:

中使用
public class Page1 : Page{
    public Page1(){
        this.renter = (IRenter)CompositionRoot.DiContainer[typeof(IRenter)];
    }
    private IRenter renter;
}