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

时间:2013-05-06 16:52:04

标签: php oop design-patterns dependency-injection

在对Dependency注入困惑之后,我担心依赖于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'关键字实际上还可以?

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

3)依赖注入容器在应用程序中的位置和方式如何?

你从什么角度实际使用/调用DI容器?似乎将DI容器硬编码到您的应用程序中?或者这实际上是重点?

希望我的问题不要模糊。

提前致谢。

1 个答案:

答案 0 :(得分:0)

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

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

该实现不仅使您的调用者依赖于house类,而且还依赖于houseFactory类,这是一种无用的依赖。

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

  
    

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

  
  1. 使对象创建的范围变得非常小。组合根通常只包含启动应用程序的小逻辑。也许还有一些其他逻辑来处理一般异常而不是其他异常。因此,创建对象是安全的,因为那里不存在特定的业务逻辑。
  2. 组合根未在其他类中创建。它被称为应用程序启动,因此在那里的类中创建对象是安全的。
  3. 要了解更多关于组合根的信息,您可以阅读Mark Seeman的书或他的博客。

      
        

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

      

    有些应用程序对DI有很好的支持,比如ASP.Net MVC,但有些不像ASP.Net Webform。在具有良好支持的应用程序中,您可以直接从composition root注入类。但是,如果没有,也许你可以通过静态类访问组合根DI容器,但只能在页面的构造函数中访问。因此,它最大限度地减少了对组合根的访问。

    EDIT1:

    以下是没有用PHP编写的DI Container的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#console中的示例:

    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.Net webforms这样的页面中的DI容器,你应该使用静态,但只在构造函数中分配对象。示例(在C#中,我不知道如何在php上使用static)。

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

    此实现仍然安全,因为DIContainer仅用于页面的构造函数级别。但请确保不要在服务类中执行此操作。例如:

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

    是一个糟糕的设计。 Renter类依赖于DIContainer。要解决这个问题,请注入IHouse类。

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

    然后Renter类的调用者应该通过house类。例如,使用穷人将IRenter类分配给DIContainer。

    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;
    }