Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding IoC, DI and reference methods

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;
    }
like image 972
Jonathan Avatar asked Oct 24 '11 20:10

Jonathan


People also ask

What is difference between IoC and DI?

Dependency Injection is the method of providing the dependencies and Inversion of Control is the end result of Dependency Injection. IoC is a design principle where the control flow of the program is inverted. Dependency Injection is one of the subtypes of the IOC principle.

What are the 3 types of DI?

Types of DI There are three main styles of dependency injection, according to Fowler: Constructor Injection (also known as Type 3), Setter Injection (also known as Type 2), and Interface Injection (also known as Type 1).

How do you explain IoC?

Inversion of control is a software design principle that asserts a program can benefit in terms of pluggability, testability, usability and loose coupling if the management of an application's flow is transferred to a different part of the application.

What do IoC and DI stand for?

Overview. In this tutorial, we'll introduce the concepts of IoC (Inversion of Control) and DI (Dependency Injection), as well as take a look at how these are implemented in the Spring framework.


1 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.

like image 112
rickchristie Avatar answered Sep 29 '22 18:09

rickchristie