Die Abhängigkeitsinjektion des Laravel-Frameworks ist in der Tat sehr leistungsstark, und die Abhängigkeitsinjektion über den Container kann die erforderlichen Dienste selektiv laden und die Kosten für die Initialisierung des Frameworks senken. Das Folgende ist ein Beitrag, den ich im Internet gesehen habe Einfach zu schreiben. Der Artikel beginnt mit dem Entwurf der Datenbankverbindung gemäß der traditionellen Klasse und zeigt die Leistungsfähigkeit der Abhängigkeitsinjektion, die unserer Referenz würdig ist und Lernen.
---------------- ------ ---------------Unter der Trennlinie befindet sich Daniels Originaltext--------------------- ------ -----------------------------------
Zunächst gehen wir davon aus dass wir eine Komponente namens SomeComponent entwickeln wollen. In diese Komponente wird nun eine Datenbankverbindung eingefügt. In diesem Beispiel wird die Datenbankverbindung in der Komponente erstellt. Diese Methode ist unpraktisch. Wenn wir dies tun, können wir einige Parameter wie Datenbankverbindungsparameter und Datenbanktyp nicht ändern.
1 <?php 2 3 class SomeComponent 4 { 5 6 PRotected $_connection; 7 8 /** 9 * Sets the connection externally 10 */ 11 public function setConnection($connection) 12 { 13 $this->_connection = $connection; 14 } 15 16 public function someDbTask() 17 { 18 $connection = $this->_connection; 19 20 // ... 21 } 22 23 } 24 25 $some = new SomeComponent(); 26 27 //Create the connection 28 $connection = new Connection(array( 29 "host" => "localhost", 30 "username" => "root", 31 "password" => "secret", 32 "dbname" => "invo" 33 )); 34 35 //Inject the connection in the component 36 $some->setConnection($connection); 37 38 $some->someDbTask();
Betrachten wir nun ein Problem: Wir verwenden diese Komponente an verschiedenen Stellen in der Anwendung und die Datenbankverbindung wird mehrmals erstellt. Verwenden Sie eine Methode ähnlich der globalen Registrierung, um von hier aus eine Datenbankverbindungsinstanz abzurufen, anstatt sie erst nach der Verwendung zu erstellen.
1 <?php 2 3 class Registry 4 { 5 6 /** 7 * Returns the connection 8 */ 9 public static function getConnection() 10 { 11 return new Connection(array( 12 "host" => "localhost", 13 "username" => "root", 14 "password" => "secret", 15 "dbname" => "invo" 16 )); 17 } 18 19 } 20 21 class SomeComponent 22 { 23 24 protected $_connection; 25 26 /** 27 * Sets the connection externally 28 */ 29 public function setConnection($connection){ 30 $this->_connection = $connection; 31 } 32 33 public function someDbTask() 34 { 35 $connection = $this->_connection; 36 37 // ... 38 } 39 40 } 41 42 $some = new SomeComponent(); 43 44 //Pass the connection defined in the registry 45 $some->setConnection(Registry::getConnection()); 46 47 $some->someDbTask();
Stellen wir uns nun vor, dass wir zwei Methoden in der Komponente implementieren müssen, von denen wir zunächst eine neue Datenbankverbindung erstellen müssen und die zweite immer eine gemeinsame Verbindung erhält:
1 <?php 2 3 class Registry 4 { 5 6 protected static $_connection; 7 8 /** 9 * Creates a connection 10 */ 11 protected static function _createConnection() 12 { 13 return new Connection(array( 14 "host" => "localhost", 15 "username" => "root", 16 "password" => "secret", 17 "dbname" => "invo" 18 )); 19 } 20 21 /** 22 * Creates a connection only once and returns it 23 */ 24 public static function getSharedConnection() 25 { 26 if (self::$_connection===null){ 27 $connection = self::_createConnection(); 28 self::$_connection = $connection; 29 } 30 return self::$_connection; 31 } 32 33 /** 34 * Always returns a new connection 35 */ 36 public static function getNewConnection() 37 { 38 return self::_createConnection(); 39 } 40 41 } 42 43 class SomeComponent 44 { 45 46 protected $_connection; 47 48 /** 49 * Sets the connection externally 50 */ 51 public function setConnection($connection){ 52 $this->_connection = $connection; 53 } 54 55 /** 56 * This method always needs the shared connection 57 */ 58 public function someDbTask() 59 { 60 $connection = $this->_connection; 61 62 // ... 63 } 64 65 /** 66 * This method always needs a new connection 67 */ 68 public function someOtherDbTask($connection) 69 { 70 71 } 72 73 } 74 75 $some = new SomeComponent(); 76 77 //This injects the shared connection 78 $some->setConnection(Registry::getSharedConnection()); 79 80 $some->someDbTask(); 81 82 //Here, we always pass a new connection as parameter 83 $some->someOtherDbTask(Registry::getConnection());
Bisher haben wir gesehen, wie wir die Abhängigkeitsinjektion nutzen können, um unser Problem zu lösen. Anstatt eine Abhängigkeit innerhalb des Codes zu erstellen, übergeben wir sie als Parameter, was die Wartung unseres Programms erleichtert, die Kopplung des Programmcodes verringert und eine Art lose Kopplung erreicht. Doch auf lange Sicht bringt diese Form der Abhängigkeitsinjektion auch einige Nachteile mit sich.
Wenn die Komponente beispielsweise viele Abhängigkeiten aufweist, müssen wir mehrere zu übergebende Setter-Methoden oder einen zu übergebenden Konstruktor erstellen. Darüber hinaus müssen Sie jedes Mal, wenn Sie eine Komponente verwenden, abhängige Komponenten erstellen, was die Codepflege nicht einfach macht. Der Code, den wir schreiben, sieht möglicherweise so aus:
1 <?php 2 3 //Create the dependencies or retrieve them from the registry 4 $connection = new Connection(); 5 $session = new Session(); 6 $fileSystem = new FileSystem(); 7 $filter = new Filter(); 8 $selector = new Selector(); 9 10 //Pass them as constructor parameters 11 $some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector); 12 13 // ... or using setters 14 15 $some->setConnection($connection); 16 $some->setSession($session); 17 $some->setFileSystem($fileSystem); 18 $some->setFilter($filter); 19 $some->setSelector($selector);
Ich denke, wir müssen erstellen An vielen Stellen in der Anwendung wird dieses Objekt verwendet. Wenn Sie die abhängigen Komponenten nicht benötigen, müssen wir zum Code-Injection-Teil gehen, um die Parameter im Konstruktor oder in der Setter-Methode zu entfernen. Um dieses Problem zu lösen, verwenden wir erneut eine globale Registrierung, um die Komponente zu erstellen. Bevor das Objekt erstellt wird, wird jedoch eine neue Abstraktionsebene hinzugefügt:
1 <?php 2 3 class SomeComponent 4 { 5 6 // ... 7 8 /** 9 * Define a factory method to create SomeComponent instances injecting its dependencies 10 */ 11 public static function factory() 12 { 13 14 $connection = new Connection(); 15 $session = new Session(); 16 $fileSystem = new FileSystem(); 17 $filter = new Filter(); 18 $selector = new Selector(); 19 20 return new self($connection, $session, $fileSystem, $filter, $selector); 21 } 22 23 }
In diesem Moment scheinen wir wieder am Anfang des Problems zu stehen, wir erstellen Abhängigkeiten innerhalb der Komponente, die wir Jedes Mal ändern und nach einer Möglichkeit suchen, das Problem zu lösen, aber das ist kein guter Ansatz.
Eine praktische und elegante Möglichkeit, diese Probleme zu lösen, ist die Verwendung der Container-Abhängigkeitsinjektion. Wie wir zuvor gesehen haben, fungiert der Container als globale Registrierung und nutzt die Container-Abhängigkeitsinjektion als Brücke, um das Problem zu lösen Unser Code ist weniger gekoppelt und reduziert die Komplexität der Komponente:
1 <?php 2 3 class SomeComponent 4 { 5 6 protected $_di; 7 8 public function __construct($di) 9 { 10 $this->_di = $di; 11 } 12 13 public function someDbTask() 14 { 15 16 // Get the connection service 17 // Always returns a new connection 18 $connection = $this->_di->get('db'); 19 20 } 21 22 public function someOtherDbTask() 23 { 24 25 // Get a shared connection service, 26 // this will return the same connection everytime 27 $connection = $this->_di->getShared('db'); 28 29 //This method also requires a input filtering service 30 $filter = $this->_db->get('filter'); 31 32 } 33 34 } 35 36 $di = new Phalcon\DI(); 37 38 //Register a "db" service in the container 39 $di->set('db', function(){ 40 return new Connection(array( 41 "host" => "localhost", 42 "username" => "root", 43 "password" => "secret", 44 "dbname" => "invo" 45 )); 46 }); 47 48 //Register a "filter" service in the container 49 $di->set('filter', function(){ 50 return new Filter(); 51 }); 52 53 //Register a "session" service in the container 54 $di->set('session', function(){ 55 return new Session(); 56 }); 57 58 //Pass the service container as unique parameter59 $some = new SomeComponent($di); 60 61 $some->someTask();
Jetzt benötigt die Komponente ihn nur, wenn sie auf einen bestimmten Dienst zugreift. Wenn sie ihn nicht benötigt, wird er nicht einmal initialisiert, um Ressourcen zu sparen . Das Bauteil ist stark entkoppelt. Ihr Verhalten oder andere Aspekte davon haben keinen Einfluss auf die Komponenten selbst.
Unsere Implementierungsmethode¶
PhalconDI ist eine Komponente, die die Abhängigkeitsinjektionsfunktion von Diensten implementiert, und es ist auch selbst ein Container.
Aufgrund des hohen Entkopplungsgrads von Phalcon ist PhalconDI ein wesentlicher Bestandteil des Frameworks, das zur Integration anderer Komponenten verwendet wird. Entwickler können diese Komponente auch zur Abhängigkeitsinjektion und zur Verwaltung von Instanzen verschiedener Klassendateien in der Anwendung verwenden.
Grundsätzlich implementiert diese Komponente das Inversion of Control-Muster. Auf dieser Grundlage implementiert das Objekt die Injektion nicht mehr durch den Empfang von Parametern im Konstruktor oder die Verwendung von Settern, sondern fordert direkt die Abhängigkeitsinjektion des Dienstes an. Dadurch wird die Gesamtkomplexität des Programms erheblich reduziert, da es nur einen Weg gibt, die erforderlichen Abhängigkeiten für eine Komponente zu erhalten.
Darüber hinaus verbessert dieses Muster die Testbarkeit des Codes und macht ihn weniger fehleranfällig.
Dienste in Containern registrieren¶
Dienste können vom Framework selbst oder von Entwicklern registriert werden. Wenn eine Komponente A einen Aufruf von Komponente B (oder einer Instanz ihrer Klasse) anfordert, kann sie einen Aufruf von Komponente B vom Container anfordern, anstatt eine Instanz von Komponente B zu erstellen.
Diese Arbeitsweise bietet uns viele Vorteile:
Wir können eine Komponente ersetzen, entweder von uns selbst oder einfach von einem Dritten erstellt.
Bevor die Komponente freigegeben wird, können wir die Initialisierung des Objekts vollständig steuern und verschiedene Einstellungen für das Objekt vornehmen.
Wir können eine einheitliche Methode verwenden, um eine strukturierte globale Instanz von der Komponente abzurufen
Dienste können auf folgende Weise in den Container eingefügt werden:
1 <?php 2 3 //Create the Dependency Injector Container 4 $di = new Phalcon\DI(); 5 6 //By its class name 7 $di->set("request", 'Phalcon\Http\Request'); 8 9 //Using an anonymous function, the instance will lazy loaded 10 $di->set("request", function(){ 11 return new Phalcon\Http\Request(); 12 }); 13 14 //Registering directly an instance 15 $di->set("request", new Phalcon\Http\Request()); 16 17 //Using an array definition 18 $di->set("request", array( 19 "className" => 'Phalcon\Http\Request' 20 ));
Im obigen Beispiel: Wenn das Framework Zugriff auf Anforderungsdaten anfordert, ermittelt es zunächst, ob ein Dienst mit dem Namen „reqeust“ im Container vorhanden ist.
Der Container gibt eine Instanz der Anforderungsdaten zurück und Entwickler erhalten schließlich die gewünschte Komponente.
Jede Methode im obigen Beispiel hat Vor- und Nachteile, welche verwendet werden soll, hängt vom jeweiligen Szenario während des Entwicklungsprozesses ab.
用一个字符串来设定一个服务非常简单,但缺少灵活性。设置服务时,使用数组则提供了更多的灵活性,而且可以使用较复杂的代码。lambda函数是两者之间一个很好的平衡,但也可能导致更多的维护管理成本。
Phalcon\DI 提供服务的延迟加载。除非开发人员在注入服务的时候直接实例化一个对象,然后存存储到容器中。在容器中,通过数组,字符串等方式存储的服务都将被延迟加载,即只有在请求对象的时候才被初始化。
1 <?php 2 3 //Register a service "db" with a class name and its parameters 4 $di->set("db", array( 5 "className" => "Phalcon\Db\Adapter\Pdo\MySQL", 6 "parameters" => array( 7 "parameter" => array( 8 "host" => "localhost", 9 "username" => "root", 10 "password" => "secret", 11 "dbname" => "blog" 12 ) 13 ) 14 )); 15 16 //Using an anonymous function 17 $di->set("db", function(){ 18 return new Phalcon\Db\Adapter\Pdo\Mysql(array( 19 "host" => "localhost", 20 "username" => "root", 21 "password" => "secret", 22 "dbname" => "blog" 23 )); 24 });
以上这两种服务的注册方式产生相同的结果。然后,通过数组定义的,在后面需要的时候,你可以修改服务参数:
1 <?php 2 3 $di->setParameter("db", 0, array( 4 "host" => "localhost", 5 "username" => "root", 6 "password" => "secret" 7 ));
从容器中获得服务的最简单方式就是使用”get”方法,它将从容器中返回一个新的实例:
1 <?php 2 $request = $di->get("request")
或者通过下面这种魔术方法的形式调用:
1 <?php 2 3 $request = $di->getRequest(); 4 5 Phalcon\DI 同时允许服务重用,为了得到一个已经实例化过的服务,可以使用 getShared() 方法的形式;
具体的 Phalcon\Http\Request 请求示例:
1 <?php 2 3 $request = $di->getShared("request");
参数还可以在请求的时候通过将一个数组参数传递给构造函数的方式:
1 <?php 2 3 $component = $di->get("MyComponent", array("some-parameter", "other"))
理解PHP依赖注入|LaravelIoC容器