Laravel フレームワークの依存関係注入は確かに非常に強力で、コンテナーを介して依存関係注入を実装すると、必要なサービスを選択的にロードし、フレームワークの初期化のオーバーヘッドを削減できます。インターネット上でよく書かれた投稿を見つけたので、それを共有したいと思います。この記事は、従来のクラスに従ってデータベース接続を設計するところから始まり、コンテナを介してサービスを読み込むという高度に分離された設計で終わります。これは依存関係の注入の力を示しており、注目に値します。
------------------------------------------------- ----------分割線の下はダニエルの原文です---------------------------- ------------------------
元リンク(http://www.yuansir-web.com/2014/03/20)
まず、SomeComponent という名前のコンポーネントを開発するとします。データベース接続がこのコンポーネントに挿入されます。この例では、データベース接続はコンポーネント内で作成されますが、これを行うと、データベース接続パラメータやデータベース タイプなどの一部のパラメータを変更できなくなります。
リーリー上記の問題を解決するには、使用前に外部接続を作成し、それをコンテナーに注入する必要があります。今のところ、これは良い解決策のように見えます:
リーリーここで問題を考えてみましょう。このコンポーネントをアプリケーション内のさまざまな場所で使用すると、データベース接続が複数回作成されます。データベース接続インスタンスを使用後に作成するのではなく、グローバル レジストリと同様の方法を使用して、ここからデータベース接続インスタンスを取得します。
リーリーここで、コンポーネントに 2 つのメソッドを実装する必要があると想像してみましょう。最初のメソッドは新しいデータベース接続を作成する必要があり、2 番目のメソッドは常に共有接続を取得します。 リーリー
これまで、依存関係注入を使用して問題を解決する方法を見てきました。コード内に依存関係を作成する代わりに、依存関係をパラメーターとして渡します。これにより、プログラムの保守が容易になり、プログラム コードの結合が減少し、一種の疎結合が実現します。しかし、長期的には、この形式の依存性注入にはいくつかの欠点もあります。例えば、コンポーネントに依存関係が多い場合、渡すsetterメソッドを複数作成したり、渡すコンストラクタを作成したりする必要があります。さらに、コンポーネントを使用するたびに依存コンポーネントを作成する必要があるため、コードのメンテナンスは次のようになります。 リーリー
このオブジェクトはアプリケーション内のいろいろな場所に作成する必要があると思います。依存コンポーネントが必要ない場合は、コード挿入部分に移動して、コンストラクターまたはセッター メソッドのパラメーターを削除する必要があります。この問題を解決するには、もう一度グローバル レジストリを使用してコンポーネントを作成します。ただし、オブジェクトを作成する前に、新しい抽象化レイヤーが追加されます。
リーリー現時点では、問題の始まりに戻っているようです。コンポーネント内に依存関係を作成し、毎回問題の解決策を探していますが、これは良いアプローチではありません。
これらの問題を解決する実用的かつ洗練された方法は、コンテナ依存関係注入を使用することです。前述したように、コンテナ依存関係注入をブリッジとして使用して依存関係を解決すると、コード結合が低くなります。コンポーネントの複雑さが大幅に軽減されます:
リーリー現在、コンポーネントは特定のサービスにアクセスする場合にのみそれを必要とします。必要がない場合は、リソースを節約するために初期化されることもありません。コンポーネントは高度に分離されています。コンポーネントの動作やその他の側面は、コンポーネント自体には影響しません。
実装方法¶
PhalconDIはサービスの依存性注入機能を実装するコンポーネントであり、それ自体がコンテナでもあります。
基本的に、このコンポーネントは制御の反転パターンを実装します。これに基づいて、オブジェクトはコンストラクターでパラメーターを受け取ったり、セッターを使用してインジェクションを実装するのではなく、サービスの依存関係インジェクションを直接要求します。コンポーネントの必要な依存関係を取得する方法が 1 つしかないため、これによりプログラム全体の複雑さが大幅に軽減されます。
さらに、このパターンによりコードのテスト容易性が向上し、エラーが発生しにくくなります。
コンテナへのサービスの登録¶
サービスはフレームワーク自体または開発者によって登録できます。コンポーネント A がコンポーネント B (またはそのクラスのインスタンス) への呼び出しをリクエストする場合、コンポーネント B のインスタンスを作成する代わりに、コンテナからコンポーネント B への呼び出しをリクエストできます。
自社またはサードパーティで簡単に作成されたコンポーネントを置き換えることができます。
コンポーネントがリリースされる前に、オブジェクトの初期化を完全に制御し、オブジェクトのさまざまな設定を行うことができます。
統一された方法でコンポーネントから構造化されたグローバルインスタンスを取得できます
サービスは次の方法でコンテナに注入できます:
<span> 1</span> <?<span>php </span><span> 2</span> <span> 3</span> <span>//</span><span>Create the Dependency Injector Container</span> <span> 4</span> <span>$di</span> = <span>new</span><span> Phalcon\DI(); </span><span> 5</span> <span> 6</span> <span>//</span><span>By its class name</span> <span> 7</span> <span>$di</span>->set("request", 'Phalcon\Http\Request'<span>); </span><span> 8</span> <span> 9</span> <span>//</span><span>Using an anonymous function, the instance will lazy loaded</span> <span>10</span> <span>$di</span>->set("request", <span>function</span><span>(){ </span><span>11</span> <span>return</span> <span>new</span><span> Phalcon\Http\Request(); </span><span>12</span> <span>}); </span><span>13</span> <span>14</span> <span>//</span><span>Registering directly an instance</span> <span>15</span> <span>$di</span>->set("request", <span>new</span><span> Phalcon\Http\Request()); </span><span>16</span> <span>17</span> <span>//</span><span>Using an array definition</span> <span>18</span> <span>$di</span>->set("request", <span>array</span><span>( </span><span>19</span> "className" => 'Phalcon\Http\Request' <span>20</span> ));
在上面的例子中,当向框架请求访问一个请求数据时,它将首先确定容器中是否存在这个”reqeust”名称的服务。
容器会反回一个请求数据的实例,开发人员最终得到他们想要的组件。
在上面示例中的每一种方法都有优缺点,具体使用哪一种,由开发过程中的特定场景来决定的。
用一个字符串来设定一个服务非常简单,但缺少灵活性。设置服务时,使用数组则提供了更多的灵活性,而且可以使用较复杂的代码。lambda函数是两者之间一个很好的平衡,但也可能导致更多的维护管理成本。
Phalcon\DI 提供服务的延迟加载。除非开发人员在注入服务的时候直接实例化一个对象,然后存存储到容器中。在容器中,通过数组,字符串等方式存储的服务都将被延迟加载,即只有在请求对象的时候才被初始化。
<span> 1</span> <?<span>php </span><span> 2</span> <span> 3</span> <span>//</span><span>Register a service "db" with a class name and its parameters</span> <span> 4</span> <span>$di</span>->set("db", <span>array</span><span>( </span><span> 5</span> "className" => "Phalcon\Db\Adapter\Pdo\Mysql", <span> 6</span> "parameters" => <span>array</span><span>( </span><span> 7</span> "parameter" => <span>array</span><span>( </span><span> 8</span> "host" => "localhost", <span> 9</span> "username" => "root", <span>10</span> "password" => "secret", <span>11</span> "dbname" => "blog" <span>12</span> <span> ) </span><span>13</span> <span> ) </span><span>14</span> <span>)); </span><span>15</span> <span>16</span> <span>//</span><span>Using an anonymous function</span> <span>17</span> <span>$di</span>->set("db", <span>function</span><span>(){ </span><span>18</span> <span>return</span> <span>new</span> Phalcon\Db\Adapter\Pdo\<span>Mysql</span>(<span>array</span><span>( </span><span>19</span> "host" => "localhost", <span>20</span> "username" => "root", <span>21</span> "password" => "secret", <span>22</span> "dbname" => "blog" <span>23</span> <span> )); </span><span>24</span> });
以上这两种服务的注册方式产生相同的结果。然后,通过数组定义的,在后面需要的时候,你可以修改服务参数:
<span>1</span> <?<span>php </span><span>2</span> <span>3</span> <span>$di</span>->setParameter("db", 0, <span>array</span><span>( </span><span>4</span> "host" => "localhost", <span>5</span> "username" => "root", <span>6</span> "password" => "secret" <span>7</span> ));
从容器中获得服务的最简单方式就是使用”get”方法,它将从容器中返回一个新的实例:
<span>1</span> <?<span>php </span><span>2</span> <span>$request</span> = <span>$di</span>->get("request");
或者通过下面这种魔术方法的形式调用:
<span>1</span> <?<span>php </span><span>2</span> <span>3</span> <span>$request</span> = <span>$di</span>-><span>getRequest(); </span><span>4</span> <span>5</span> Phalcon\DI 同时允许服务重用,为了得到一个已经实例化过的服务,可以使用 getShared() 方法的形式来获得服务。
具体的 Phalcon\Http\Request 请求示例:
<span>1</span> <?<span>php </span><span>2</span> <span>3</span> <span>$request</span> = <span>$di</span>->getShared("request");
参数还可以在请求的时候通过将一个数组参数传递给构造函数的方式:
<span>1</span> <?<span>php </span><span>2</span> <span>3</span> <span>$component</span> = <span>$di</span>->get("MyComponent", <span>array</span>("some-parameter", "other"))