First of all rely on injection and control inversion is said to be the same thing, is a design pattern, this design pattern to reduce the coupling between the program, I learned a bit, see TP official website has no relevant articles, write this my book introduce this design mode, hope to contribute some power to TP community.
First of all, do not investigate the definition of the design pattern, otherwise you will be said Foggy, the author is deeply affected by its harm, Baidu has more than n articles, are described from a theoretical point of view, flooded with a lot of jerky words, or Java code description, but also jerky.
Anyway, to figure it out, let's describe the concept of dependency injection in PHP's perspective.
Let's say we have a class here that requires a database connection in the class, and in the most primitive way, we might write this class:
?
1234567891011 |
class example {
private $_db
;
function __construct(){
include "./Lib/Db.php"
;
$this
->_db =
new Db(
"localhost"
,
"root"
,
"123456"
,
"test"
);
}
function getList(){
$this
->_db->query(
"......"
);
//这里具体sql语句就省略不写了
}
}
|
Process:
In the constructor, the database class file include in the first;
The DB class is then instantiated via new DB and incoming database connection information;
The GetList method can then invoke the database class through $this->_db to implement the database operation.
It seems that we have achieved the desired function, but this is the beginning of a nightmare, later example1,example2,example3 .... More and more classes need to use the DB component, if this is the case, if one day the database password changed or DB class changed, would you like to go back to modify all the class files?
OK, in order to solve this problem, the factory pattern appears, we create a factory method, and use the Factory::getdb () method to obtain an instance of the DB component:
?
123456 |
class Factory {
public static function getDb(){
include "./Lib/Db.php"
;
return new Db(
"localhost"
,
"root"
,
"123456"
,
"test"
);
}
}
|
The sample class becomes:
?
12345678910 |
class example {
private $_db
;
function __construct(){
$this
->_db = Factory::getDb();
}
function getList(){
$this
->_db->query(
"......"
);
//这里具体sql语句就省略不写了
}
}
|
Is that perfect for you? Think again about later example1,example2,example3 .... For all classes, you need to pass FACTORY::GETDB () in the constructor, and in fact you have the coupling of the original direct and DB class to the Factory factory class, the factory class just wraps up the database connection information for you, Although the database information changes as long as the Factory::getdb () method can be modified, but suddenly one day the factory method needs to change the name, or the Getdb method needs renaming, what do you do? Of course, this demand is still very fucked up, but sometimes it does, one solution is:
We do not instantiate the DB component from within the example class, we rely on external injection, what does it mean? Look at the following example:
?
1234567891011121314 |
class example {
private $_db
;
function getList(){
$this
->_db->query(
"......"
);
//这里具体sql语句就省略不写了
}
//从外部注入db连接
function setDb(
$connection
){
$this
->_db =
$connection
;
}
}
//调用
$example =
new example();
$example
->setDb(Factory::getDb());
//注入db连接
$example
->getList();
|
In this way, the example class is completely decoupled from the external class, and you can see that there are no factory methods or DB classes in the DB class. We inject the connection instance directly into it by invoking the Setdb method of the example class from the outside. In this way the example does not care at all how the DB connection was generated.
This is called dependency injection, and instead of creating a dependency within the code, the implementation is passed as a parameter, which makes our program easier to maintain, reduces the coupling of the program code, and implements a loose coupling.
It's not over yet, and we'll assume that the example class uses other external classes in addition to DB, and we pass:
?
1234 |
$example ->setDb(Factory::getDb()); //注入db连接 $example ->setFile(Factory::getFile()); //注入文件处理类 $example ->setImage(Factory::getImage()); //注入Image处理类 ... |
We've been writing so many sets for so long? Tired or tired?
OK, in order not to write so many lines of code each time, we went to get a factory method:
?
123456789 |
class Factory {
public static function getExample(){
$example =
new example();
$example
->setDb(Factory::getDb());
//注入db连接
$example
->setFile(Factory::getFile());
//注入文件处理类
$example
->setImage(Factory::getImage());
//注入Image处理类
return $expample
;
}
}
|
When instantiating example, it becomes:
?
12 |
$example =Factory::getExample(); $example ->getList(); |
It seems perfect, but how does it feel to go back to the scene when I first used the factory method? This is really not a good solution, so a concept is proposed: containers, also called IOC containers, di containers.
We originally injected various classes through the Setxxx method, the code is very long, a lot of methods, although it can be packaged through a factory method, but not so cool, well, we do not have to setxxx method, so also do not have to factory method two times packaging, then we also how to achieve dependency injection?
Here we introduce a convention: in the constructor of the example class, pass in a parameter named Di $di, as follows:
?
12345678910111213141516 |
class example {
private $_di
;
function __construct(Di &
$di
){
$this
->_di =
$di
;
}
//通过di容器获取db实例
function getList(){
$this
->_di->get(
‘db‘
)->query(
"......"
);
//这里具体sql语句就省略不写了
}
}
$di =
new Di();
$di
->set(
"db"
,
function
(){
return new Db(
"localhost"
,
"root"
,
"root"
,
"test"
);
});
$example =
new example(
$di
);
$example
->getList();
|
DI is the IOC container, the so-called container is to store the various classes we may use, we set a instance named DB by $di->set (), because it is passed through the callback function, so the set does not immediately instantiate the DB class, but when the $di-> Get (' db ') is instantiated, similarly, when designing the Di class, you can also incorporate a singleton pattern.
So we just declare a DI class at the global scope, put all the classes that need to be injected into the container, and then pass the container as a constructor parameter to example, and get the instance from the container in the example class. Of course, it is not necessarily a constructor, you can also use a setdi (di $di) method to pass in the Di container, in short, the agreement is you make, you know the line.
PHP Dependency Injection (DI) and inversion of Control (IoC) detailed