Home » Php » php – Understanding IoC, DI and reference methods

php – Understanding IoC, DI and reference methods

Posted by: admin July 12, 2020 Leave a comment

Questions:

I’m in the process of learning depenency injection and inversion of control, and I think I’m starting to understand how this works:

  • Objects should not concern themselves with the creation of their own dependencies
  • Dependencies should be passed to the object (via the constructor or setter methods)
  • A DI container can do the work of creating objects with all of their required dependencies

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

Here is what I mean by reference methods. Say I have two models for families and family members. I find it very helpful to create methods that reference objects that relate to that model. In the example below, when calling $family->members(), I can quickly gain access to all the family members. But, this would mean that my family object is instantiating family_member classes…and doesn’t this break the rules of IoC?

What if the family_member class had a dependency that was outside of the scope of the family class? Input here would be much appriciated!

<?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;
    }
How to&Answers:

Disclaimer: I’m just learning DI myself. Take the answer with a grain of salt.

Dependency injection is only about injecting dependencies. If your object oriented design results in Family object having the responsibility to create instances of Member, then by all means, have the Family object create the Member, because in that case, Member is no longer considered a dependency of Family, but a responsibility. 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, does Family really should have the responsibility of creating Member? A better design is to have another object, such as FamilyMapper create Family along with its members. Like this:

class FamilyMapper
{
    /**
     * Constructor.
     * 
     * A better OO design, imho is using the DataMapper pattern.
     * The mapper's responsibility is instantiating Family,
     * which means it's 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 pattern, your domain objects, along with their methods (which may contain business logic) will be decoupled from your data access code. That’s the good thing about dependency injection – it forces you to rethink your OO design, so that you end up with cleaner code.

Many people think that using dependency injection means not using factories and such. This is wrong! Dependency injection is only about injecting dependencies. You can use dependency injection with factory objects too, by injecting dependencies to the factory instead of having the factory instantiating its own dependency.

Useful links:

  1. http://martinfowler.com/articles/injection.html
  2. Does anyone have a good analogy for dependency injection?
  3. How to explain dependency injection to a 5-year-old?

Additions

Again, take the stuff below here with a grain of salt.

Please also note that there is a difference between dependency injection and dependency injection container. The first one is a simple concept of injecting dependencies instead of having objects creating it themselves (which results in very high coupling). We see this from the example above.

The latter is a term for frameworks/libraries that deal with dependency injection so you don’t have to do manual injection. The container’s responsibility is wiring dependencies so you don’t have to do the dirty work. The idea is you define a dependency injection configuration, which tells the container what dependencies Foo object has, and how to inject them. The container reads the documentation and performs the injection for you. This is what DIC libraries like Pimple, SimpleDIC do.

You can compare dependency injection containers with factories, since both are a creational objects, whose sole responsibility is to create objects. While factories are often specialized (i.e. FamilyMemberFactory creating instances of MemberInterface), dependency injection container is more general. Some people say using dependency injection container relieves you of the need for factories, but you should remember that it means you have to create and maintain dependency injection configuration files, which could be thousands of XML/PHP lines.

I hope this helps.

Answer:

While you can go with dependency injection only and all the way, trying to find a balance in your design while keeping it maintainable is a more sane approach, in my opinion.

As such, a combination of dependency injection and factories will make your life much easier.

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. While dependency injection frees your object from creating its dependencies, the factory pattern lets it create a base implementation of a dependency. And in the end, it still allows you to override object dependencies when required:

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

Dependency injection is great, but it cannot solve all your design problems. Of course, they are not interchangeable. The factory pattern do things DI can’t and vice-versa.

Answer:

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

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

Basically, everything sums up to the idea that components should be ignorants of their dependencies, and in your case, Family depends on FamilyMember, so any FamilyMember implementation needs to be abstracted from the Family itself.

For the specific case of PHP, consider checking out Symfony, which does heavy use of the DI pattern or you can consider using the Symfony Dependency Injection framework, which is a part of Symfony, but can be used as a separate component. Also Fabien Potencier has written a nice article on the matter.

Hope I can help!