Understanding IoC, DI, and Reference Methods

I participate in the process of learning the outflow and inversion of control, and I think I'm starting to understand how this works:

  • Objects do not have to worry about creating their own dependencies
  • Dependencies must be passed to the object (via constructor or setter methods)
  • A DI container can do the job of creating objects with all the necessary dependencies.

If all this is correct, can I no longer use what I call "reference methods" in my objects?

Here is what I mean by reference methods. Let's say I have two models for families and family members. It is very useful for me to create methods that reference objects related to this model. In the example below, when calling $family->members() I can quickly access all family members. But that would mean that my family object instantiates family_member classes ... and doesn't that violate the IoC rules?

What if the family_member class had a dependency that was outside the scope of the family class? Entering here will be tested a lot!

 <?php class family { public $id; public function members() { // Return an array of family_member objects } } class family_member { public $family_id; public $first_name; public $last_name; public $age; } 
+8
php dependency-injection inversion-of-control
source share
3 answers

Disclaimer: I'm just learning DI myself. Take it with salt.

Including dependencies is just dependency injection. If your object-oriented design makes the Family object responsible for creating Member instances, then be sure to create the Family object, since in this case the Member will no longer be considered a Family dependency, but it is responsible. Therefore:

 class Family { /** * Constructor. * * Since you have decided in your OO design phase that this * object should have the responsibility of creating members, * Member is no longer a dependency. MySQLi is, since you need * it to get the information to create the member. Inject it. * */ public function __construct($id, MySQLi $mysqli) { $this->id = $id; $this->mysqli = $mysqli; } /** * Query the database for members data, instantiates them and * return them. * */ public function getMembers() { // Do work using MySQLi } } 

But if you think about it, should Family really be responsible for creating a Member ? It is better to have another object, for example FamilyMapper to create a Family with its members. Like this:

 class FamilyMapper { /** * Constructor. * * A better OO design, imho is using the DataMapper pattern. * The mapper responsibility is instantiating Family, * which means it going to have to connect to the database, * which makes MySQLi its dependency. So we inject it. * */ public function __construct(MySQLi $mysqli) { $this->mysqli = $mysqli; } public function findByID($familyID) { // Query database for family and members data // Instantiate and return them } } class Family { /** * Constructor. * * Family is an object representing a Family and its members, * along with methods that *operate* on the data, so Member * in this OO design is a dependency. Inject it. * */ public function __construct($id, MemberCollection $members) { $this->id; $this->members; } public function getMembers() { return $this->members; } } 

Using this template, your domain objects along with their methods (which may contain business logic) will be separated from your data access code. This is a good thing about dependency injection - it makes you rethink your OO design so you get cleaner code.

Many people think that using dependency injection means using non-factories and so on. This is not true! Including dependencies is just injecting dependencies. You can also use dependency injection with factory objects by introducing factory dependencies instead of the factory creating its own dependency.

Useful links:


Additions

Take the salt material here again.

Also note that there is a difference between dependency injection container and dependencies. The first of these is a simple concept of injection dependencies instead of creating the objects themselves (which leads to a very high connection). We see this from the above example.

The last term for frameworks / libraries that deal with dependency injection, so you do not need to do manual injection. The responsibility of the container is depending on the wiring, so you do not need to do the dirty work. The idea is that you define a dependency injection configuration that tells the container which dependency objects are Foo and how to enter them. The container reads the documentation and injects for you. This is what DIC libraries do, such as Pimple, SimpleDIC.

You can compare dependency injection containers with factories, since both are creation objects whose only task is to create objects. Although factories often specialize (i.e. FamilyMemberFactory creates instances of MemberInterface ), the dependency injection container is more general. Some people say that using the dependency injection container eliminates the need for factories, but you should remember that this means that you need to create and maintain dependency attachment configuration files, which can be thousands of XML / PHP lines.

Hope this helps.

+5
source share

While you can only go with dependency injection completely, trying to find balance in your design while maintaining its maintainability is a more reasonable approach, in my opinion.

Thus, a combination of dependency injections and factories will greatly facilitate your life.

 class family { protected $_members; public function __construct($members = array()) { $this->_members = array_filter($members, function($obj){ return ($obj instanceof family_member); }); } public function addMember() { $this->_members[] = $member = $this->_createMember(); return $member; } protected function _createMember() { return new family_member; } } 

The factory pattern is somewhat compatible with inversion of control and dependency injection. Although dependency injection frees your object from creating dependencies, the factory pattern allows you to create a basic dependency implementation. And in the end, it still allows you to redefine the dependencies of objects if necessary:

 class familyExtended extends family { protected function _createMember() { return new familyExtended_member; } } 

This is especially useful when testing:

 class familyTestable extends family { protected function _createMember() { return new family_memberFake; } } 

Injection injection is great, but it cannot solve all your design problems. Of course, they are not interchangeable. The factory sample does what DI cannot do, and vice versa.

+1
source share

The problem is that the whole idea of ​​DI is that Family should not know how to create a specific FamilyMember , for example, you should have IFamilyMember , and implementations for it may vary for each module / level of your application.

There should be something like this:

 public function GetMembers() { $members = array(); foreach ( $member in $this->internalMemberList ){ // get the current implementation of IFamilyMember $memberImpl = $diContainer->Resolve("IFamilyMember"); $memberImpl->Name = $member->Name; //.... Not the best example.... } return $members; } 

In principle, it all comes down to the idea that components should be ignorant of their dependencies, and in your case, Family depends on FamilyMember , so any implementation of FamilyMember should be abstracted from the Family itself.

For a specific PHP case, consider Symfony , which uses the DI pattern heavily, or you might consider using the Symfony form substitution concept , which is part of Symfony but can be used as a separate component. Fabien Potencher also wrote a good article on this subject.

Hope I can help!

0
source share

All Articles