>백엔드 개발 >PHP 튜토리얼 >PHP 종속성 주입 이해 |

PHP 종속성 주입 이해 |

黄舟
黄舟원래의
2016-12-16 09:31:241038검색

Laravel 프레임워크의 종속성 주입은 실제로 매우 강력하며 컨테이너를 통한 종속성 주입은 필요한 서비스를 선택적으로 로드하고 프레임워크 초기화 비용을 줄일 수 있습니다. 다음은 인터넷에서 본 게시물입니다. 이 기사는 전통적인 클래스에 따라 데이터베이스 연결을 설계하는 것부터 시작하여 컨테이너를 통해 서비스를 로드하는 고도로 분리된 설계까지, 이는 우리가 참고할 만한 종속성 주입의 힘을 보여줍니다. 그리고 학습.

------------------------------- ------ ---------------구분선 아래는 다니엘의 원문입니다--------- ------ --------

먼저, SomeComponent 라는 구성 요소를 개발하고 싶습니다. 이제 데이터베이스 연결이 이 구성요소에 주입됩니다. 이 예에서는 데이터베이스 연결이 구성 요소에서 생성됩니다. 이 방법은 실용적이지 않습니다. 이렇게 하면 데이터베이스 연결 매개 변수 및 데이터베이스 유형과 같은 일부 매개 변수를 변경할 수 없습니다.

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();

이제 문제를 생각해 보겠습니다. 이 구성 요소를 애플리케이션의 여러 위치에서 사용하면 데이터베이스 연결이 여러 번 생성됩니다. 일단 데이터베이스 연결 인스턴스를 생성하는 대신 여기에서 전역 레지스트리와 유사한 방법을 사용하여 데이터베이스 연결 인스턴스를 얻으십시오.

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();

이제 구성 요소에 두 가지 메소드를 구현해야 한다고 가정해 보겠습니다. 그 중 첫 번째는 새 데이터베이스 연결을 생성해야 하고 두 번째 메소드는 항상 공유 연결을 얻습니다.

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());

지금까지 의존성 주입을 사용하여 문제를 해결하는 방법을 살펴보았습니다. 코드 내에서 종속성을 생성하는 대신 이를 매개변수로 전달합니다. 그러면 프로그램을 더 쉽게 유지 관리할 수 있고 프로그램 코드의 결합이 줄어들며 일종의 느슨한 결합이 달성됩니다. 그러나 장기적으로 볼 때 이러한 형태의 종속성 주입에는 몇 가지 단점도 있습니다.

예를 들어 구성 요소에 종속성이 많은 경우 전달할 setter 메서드를 여러 개 생성하거나 전달할 생성자를 생성해야 합니다. 게다가 컴포넌트를 사용할 때마다 종속 컴포넌트를 생성해야 하므로 코드 유지 관리가 쉽지 않습니다.

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);

내 생각엔

 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 }

이 개체는 응용 프로그램의 여러 위치에 있습니다. 종속 구성 요소가 필요하지 않은 경우 코드 삽입 부분으로 이동하여 생성자 또는 setter 메서드에서 매개 변수를 제거해야 합니다. 이 문제를 해결하기 위해 다시 한 번 전역 레지스트리를 사용하여 구성 요소를 만드는 방법으로 돌아갑니다. 그러나 객체를 생성하기 전에 새로운 추상화 계층을 추가합니다.

이 순간 우리는 문제의 시작 부분으로 돌아가서 구성 요소 내부에 종속성을 생성하고 있습니다. 매번 수정하고 문제를 해결할 수 있는 방법을 찾아보지만 이는 좋은 접근 방식이 아닙니다.
 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(&#39;db&#39;);
 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(&#39;db&#39;);
 28 
 29         //This method also requires a input filtering service
 30         $filter = $this->_db->get(&#39;filter&#39;);
 31 
 32     }
 33 
 34 }
 35 
 36 $di = new Phalcon\DI();
 37 
 38 //Register a "db" service in the container
 39 $di->set(&#39;db&#39;, 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(&#39;filter&#39;, function(){
 50     return new Filter();
 51 });
 52 
 53 //Register a "session" service in the container
 54 $di->set(&#39;session&#39;, function(){
 55     return new Session();
 56 });
 57 
 58 //Pass the service container as unique parameter59 $some = new SomeComponent($di);
 60 
 61 $some->someTask();

이러한 문제를 해결하는 실용적이고 우아한 방법은 컨테이너 종속성 주입을 사용하는 것입니다. 앞서 살펴본 것처럼 컨테이너는 전역 레지스트리 역할을 하며 컨테이너 종속성 주입을 브리지로 사용하여 문제를 해결할 수 있습니다. 코드가 덜 결합되고 구성 요소의 복잡성이 줄어듭니다.

이제 구성 요소는 특정 서비스에 액세스할 때만 필요합니다. 그렇지 않으면 리소스를 절약하기 위해 초기화되지도 않습니다. 구성요소가 고도로 분리되어 있습니다. 해당 동작이나 다른 측면은 구성 요소 자체에 영향을 주지 않습니다.

구현 방법¶

PhalconDI는 서비스의 의존성 주입 기능을 구현하는 컴포넌트이자 컨테이너 그 자체이기도 합니다.

Phalcon의 높은 수준의 분리로 인해 PhalconDI는 다른 구성요소를 통합하는 데 사용되는 프레임워크의 필수적인 부분입니다. 개발자는 이 구성요소를 사용하여 애플리케이션에서 다양한 클래스 파일의 인스턴스를 종속성 주입하고 관리할 수도 있습니다.


기본적으로 이 구성 요소는 Inversion of Control 패턴을 구현합니다. 이를 바탕으로 객체는 더 이상 생성자에서 매개변수를 받거나 setter를 사용하여 주입을 구현하지 않고 직접 서비스의 종속성 주입을 요청합니다. 구성 요소의 필수 종속성을 얻는 방법은 한 가지뿐이므로 전체 프로그램 복잡성이 크게 줄어듭니다.

또한 이 패턴은 코드의 테스트 가능성을 향상시켜 오류 가능성을 줄여줍니다.

컨테이너에 서비스 등록¶

서비스는 프레임워크 자체 또는 개발자가 등록할 수 있습니다. 구성 요소 A가 구성 요소 B(또는 해당 클래스의 인스턴스)에 대한 호출을 요청할 때 구성 요소 B의 인스턴스를 만드는 대신 컨테이너에서 구성 요소 B에 대한 호출을 요청할 수 있습니다.


이러한 작업 방식은 우리에게 많은 이점을 제공합니다.

자체적으로 구성 요소를 교체하거나 제3자가 쉽게 만든 구성 요소를 교체할 수 있습니다.

컴포넌트가 출시되기 전에 객체의 초기화를 완전히 제어하고 객체에 대한 다양한 설정을 지정할 수 있습니다.

통합된 방법을 사용하여 구성 요소에서 구조화된 전역 인스턴스를 가져올 수 있습니다
 1 <?php 
 2  
 3 //Create the Dependency Injector Container 
 4 $di = new Phalcon\DI(); 
 5  
 6 //By its class name 
 7 $di->set("request", &#39;Phalcon\Http\Request&#39;); 
 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" => &#39;Phalcon\Http\Request&#39;
 20 ));

서비스는 다음과 같은 방법으로 컨테이너에 주입될 수 있습니다.

위의 예에서, 프레임워크에서 요청 데이터에 대한 액세스를 요청할 때 먼저 이 "요청" 이름을 가진 서비스가 컨테이너에 존재하는지 확인합니다.

컨테이너는 요청된 데이터의 인스턴스를 반환하고 개발자는 마침내 원하는 구성 요소를 얻습니다. 위 예의 각 방법에는 장점과 단점이 있습니다. 어떤 방법을 사용할지는 개발 과정의 특정 시나리오에 따라 다릅니다.

用一个字符串来设定一个服务非常简单,但缺少灵活性。设置服务时,使用数组则提供了更多的灵活性,而且可以使用较复杂的代码。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容器

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.