DI
DI is often referred to as dependency injection, but what exactly is dependency injection?
For example, a computer (not a laptop) requires a keyboard and mouse to operate, and this’ needs’ is, in other words, ‘depends’ on the keyboard and mouse.
So, correspondingly, a class needs another class to do its job, so that’s a dependency.
Take a look at this code:
class Computer { protected $keyboard; public function __construct() { $this->$keyboard = new Keyboard(); }} The Computer class relies on the keyboard class.Copy the code
Ok, now that we know what a dependency is, what is injection?
Let’s change the code above:
class Computer { protected $keyboard; public function __construct(Keyboard $keyboard) { $this->$keyboard = $keyboard; } } $computer = new Computer(new Keyboard()); The Computer class here relies on injecting the Keyboard class.Copy the code
My understanding of dependency injection is as follows:
The required classes are passed in as parameters and are called dependency injection.
Now that we understand dependency injection, we can move on to IOC.
IOC
What is IOC?
Inversion of control in Chinese. What does that mean? And this is easy to understand when you look at DI.
Through DI, we can see that the dependent classes needed by a class are passed into the class after we actively instantiate them.
What does inversion of control have to do with this?
Inversion of control means handing over control of dependent classes from active to passive.
Take a look at the Laravel code:
namespace App\Http\Controllers; use Illuminate\Http\Request; Class SessionController extends Controller {public function login(Request $Request) { We don't need to actively pass in the class and laravel will do it}}Copy the code
Now you might wonder, how does this work?
This is the service container, please continue to read.
Service container
After reading many articles, I agree that the service container is a design pattern.
Its purpose is to decouple dependencies.
It’s kind of similar to what I said earlier about The Meta Mode. The difference is that the service container addresses all dependent implementations.
Let’s take a look at the evolution of the service container from beginning to end.
Again, computers, we know that computers rely on keyboards and mice, but there are many kinds of keyboards and mice.
Let’s take a look at the original code example:
class Computer { protected $keyboard; public function __construct($type = null) { switch($type) { case 'common': $this->keyboard = new CommonKeyboard(); case 'awesome': $this->keyboard = new AweSomeKeyboard(); default: $this->keyboard = new Keyboard(); }}}Copy the code
Maybe you see the problem right away.
If we were to add another keyboard, we would have to change this class again. At this rate, the class becomes too large and overly coupled.
So how can we change that?
- The factory pattern
This way we can avoid directly modifying the Computer class.
Public static function getInstance($type){switch($type) {case 'common': $this->keyboard = new CommonKeyboard(); break; case 'awesome': $this->keyboard = new AweSomeKeyboard(); break; default: $this->keyboard = new Keyboard(); break; } } } class Computer { protected $keyboard; public function __construct($type == null) { $this->keyboard = Factory::getInstance($type); }}Copy the code
By using the simple factory pattern, subsequent changes can be made to the factory class instead of the Computer class. This is equivalent to decoupling the Computer class.
The Computer class no longer depends on the keyboard class, but it does depend on the factory class.
The factory class must be modified later to add a new type of keyboard.
Therefore, this factory class cannot well meet the requirements. We know that the interface of the Computer to the Keyboard is the same, and the Keyboard must realize this interface to be recognized by the Computer, so we modify the Computer and Keyboard classes.
- DI (Dependency injection)
interface Board { public function type(); } class implements Board {public function type(){echo 'implements Board '; }} class MechanicalKeyboard implements Board {public function type(){echo 'MechanicalKeyboard '; } } class Computer { protected $keyboard; public function __construct (Board $keyboard) { $this->keyboard = $keyboard; } } $computer = new Computer(new MechanialKeyBoard());Copy the code
However, there is a problem with this, what if we are not satisfied with the keyboard used on this computer and need to replace it? We are back to square one and have to modify the keyboard class passed in.
Can you make it configurable?
- IOC Service Container (Gigafactory)
class Container { protected $binds; protected $instances; public function bind($abstract, $concrete) { if ($concrete instanceof Closure) { $this->binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } public function make($abstract, $parameters = []) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } array_unshift($parameters, $this); return call_user_func_array($this->binds[$abstract], $parameters); }}Copy the code
This is a simple IOC service container.
How does this solve our problem?
$container = new Container;
$container->bind('Board', function($container){
return new CommonBoard;
});
$container->bind('Computer',function($container,$module){
return new Computer($container->make($module));
});
$computer = $container->make('Computer',['Board']);
Copy the code
The Computer class produced here is a Computer class that uses a normal keyboard.
Explain the code:
bind(name,function($container){ return new Name; }}}}}}}}}}}}}}}}} The make(name) method produces an instance of name.Copy the code
What if we need to replace the keyboard?
$container->bind('Board', function($container){
return new MechanicalBoard;
});
$container->bind('Computer',function($container,$module){
return new Computer($container->make($module));
});
$computer = $container->make('Computer',['Board']);
Copy the code
With a change to the Board class implementation of the bind binding, we can easily replace the keyboard. This is a service container.
An understanding of the service container:
A container is a thing that holds things, like a bowl. And service is the bowl of food, dishes, and so on. When we need food, we can get it from this bowl. If you want to add a la carte to your meal (i.e., the meal dependency is injected into the dish), we can take the meal directly from the bowl, and these dependencies are taken care of by the container (i.e., inversion of control).
What we need to do is to maintain the service provided.
Let’s look at some real code that runs on the Laravel framework:
code
Of course, the Laravel framework’s service container is much more complex than this one, but it’s easy to start with Laravel once we understand its purpose and usage scenarios.
PS: Your praise is the motivation of my creation!