I'm in the process of learning depenency injection and inversion of control, and I think I'm starting to understand how this works:
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;
}
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.
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).
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.
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.
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With