Dependency Injection prevent dependency on the dependency container

After puzzling around with the Dependency injection I'm worried about getting dependent on the DI container. So I thought I tie everything together with factories. But there seem to be an awful a lot of factories needed (One for every object(?)).

Now what happens is the following:

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

The houseFactory basicly wires the creation and calling code together without having to know how to create a house. But is it really oke to actually call for a new factory for every needed object?

It seems so for now because lets say house isn't so easily resolved by DI container alone but needs certain rules. For example there should be only one instance of it (also known as 'shared'). Those rules would go into the factory.

My questions are:

1) Is this acceptable?

I predict a huge collection of factories and my concern is that it's going to be an overkill/huge drawback/against what it is supposed to do.

2) When is it actually 'ok' to use the 'new' keyword?

I understand why moving the logic to create a new object with dependency is good practice. But it seems circular.

3) Where and how does the dependency injection container come into play in applications?

From what point of do you actually use/call the DI container? It seems like hardcoding the DI container into your application? or is that actually the point?

Hopefully my questions aren't to vague.

Thanks in advance.


The main purpose of DI is having the dependency injected, not injected the object that responsible to resolve / create the object. In your example, say that we want to use houseFactory class to get house class and use it (example the goHome method).

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

The implementation not only makes your caller to dependent on the house class, but it is also dependent to houseFactory class, which is a useless dependency.

As TrueWill said, the only place an application responsible to do object creation is composition root . The composition root is a start point of every application. Unfortunately I don't think there is any composition root in pure php class, however there should be in php frameworks such as CI or Zend.

Why should we do object creation in composition root ?

  • to make the scope of object creation become very small. Composition root usually only contains small logic to start the application. Maybe some other logic to handle general exception but not other else. Therefore it is safe to do object creation because no specific business logic exists there.
  • the composition root does not being created in other class. It is being called as application startup, so it is safe to create objects in a class there.
  • To understand more about composition root you can read at Mark Seeman's book or his blog.

    Then how do we access the object created in composition root?

    Some application has good support to DI, such as ASP.Net MVC, however some not like ASP.Net Webform. In applications that has good support, then you can inject the class directly from composition root . However if not, maybe you can access the composition root DI Container via static class, but only at page's constructor. So it minimize the access to composition root.

    EDIT1:

    Here is usage example of house class without DI Container written in php (maybe not working, haven't used DI in php).

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

    This is the example in 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();
        }
    }
    

    EDIT 2:

    Is the DI container actually used outside the 'main' method? How do I create new objects outside the 'main' method? Lets say I am a few Objects deep. How do I create a house there?

    What I have exampled above can be used in Asp.Net MVC, where the page is also automatically resolved using an IOC Container. Unfortunately, if you need the DI Container in pages like Asp.Net webforms, you should use static, but assign the objects in constructor only. Example (in C#, I don't know how to use static at php).

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

    This implementation still safe, because the DIContainer only used at constructor level of a page. But make sure do not do this in Service class . Example:

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

    Is a bad design. The Renter class is dependent on DIContainer. To solve that, inject the IHouse class.

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

    Then the caller of Renter class should pass the house class. The example is to assign the IRenter class to DIContainer using poor man's.

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

    Then you use it in Page:

    public class Page1 : Page{
        public Page1(){
            this.renter = (IRenter)CompositionRoot.DiContainer[typeof(IRenter)];
        }
    
        private IRenter renter;
    }
    
    链接地址: http://www.djcxy.com/p/82094.html

    上一篇: 简单的依赖关系解析器

    下一篇: 依赖注入防止依赖容器的依赖