简体   繁体   中英

Dependency Injection Container and Collections

I have been searching for an answer in this topic but I haven't been able to find a satisfactory one like in other topics, where the consensus is solid.

The situation

To keep things simple: I am implementing a custom Dependency Injection Container into one of my current projects (I know, I should use an already built one, but I'm doing it with learning purposes; so answers like 'use this func of that container…' are not useful) and I've stumbled a problem with instantiation of new elements inside a collection.

The problem

Imagine that I have a complex object, for example a car. This car has several dependencies (engine, axis, seats, airbags…) that have, at the same time, their own dependencies, and so on. It is not a big issue to make the DiC (via autowiring or using a config file) build the object graph and inject all the dependencies with a simple line of code like:

$car = $container->get(‘car’);

The problem arrives when I build a CarCollection, which is a simple class that wraps an array of cars. The issue comes when I try to use a method that populates the collection with all the cars that exist in the database. It's obvios that the collection should be able to create the Car objects on the fly when we call the “getAll” method from the database. The code would be something like this:

public function populate(array $filters) {
    $all_data = $this->dao->getAll($filters); // Call the data access object to query all cars.
    foreach($all_data as $data) {
        $new_object = $this->container(‘car’); // create a template object
        $new_object->setData($data); // set the info.
        $this->items[] = $new_object; // Add to the collection.
     }
}

If car was not such a complex object it would be easier, because I could pass the car fqcn as a parameter for carCollection and use it in every iteration. But that's not possible for a very complex object (or if I want to instantiate different sub types of the object - for example: lorry, pick-up, van…- depending on information from the database).

The question.

Regarding the collection being aware about the container: does not it break the purpose of the DIC phylosophy?

I guess not on one side, because I am using PSR\Container to type hint the container I pass to the collection (which loosens the coupling). But it breaks the idea that the container should not be coupled with the domain model at all.

The only alternative that I have thought about is substituting the creation of one new object for each iteration with a cloning from a prototype object that lives in the collection as a property. But we all know cloning in php can get really tricky and very difficult to debug (Or worse: very difficult to even know that there is a problem going on).

Similar issue.

PS: I have the same problem when I try to do lazy loading using Porxy objects: I need the proxy objects to have access to the container if I want to instantiate the full object later, which also breaks the principles of a DiC.

Thank you all.

I think the core of your issue comes down to this:

The issue comes when I try to use a method that populates the collection with all the cars that exist in the database.

The composition of object graphs of application components should be independent of any I/O, as described here and here by Mark Seemann. This means that the structure of those object graphs can't (and shouldn't) change based on changes in your database.

It seems to me that your Car object is a Domain Object, rather than a (Domain) Service. It would, therefore, be better suited to return a collection of Car instances from a service, rather than injecting the collection directly into one of your application components.

I need the proxy objects to have access to the container if I want to instantiate the full object later, which also breaks the principles of a DiC.

It's generally considered to be a bad practice to let application code depend on the DI Container. This aa pattern known as the Service Locator anti-pattern . There are many reasons why Service Locator is considered to be a bad idea, but it's important to understand that the list of downsides of Service Locator do not apply when the DI Container is used in the application's startup path (aka the Composition Root ). A DI Container used within the Composition Root, is not an implementation of the Service Locator anti-pattern.

When your proxy classes are defined within the Composition Root, it's perfectly fine to let them depend on the DI Container and you won't violate any software principle or practice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM