PHP 依存関係注入について | Laravel IoC コンテナ
Laravel フレームワークの依存関係注入は確かに非常に強力で、コンテナを通じて実装された依存関係注入により、必要なサービスを選択的にロードし、フレームワークの初期化のオーバーヘッドを削減できます。以下は私がオンラインで学んだことです。非常によく書かれた投稿を見たので、それを共有したいと思います。この記事は、従来のクラスに従ってデータベース接続を設計することから始まり、コンテナを介してサービスをロードするという高度に分離された設計で終わります。これは依存関係の挿入の力を示しており、学ぶ価値があります。
------------------------------------------ ------ ---------------分割線の下はダニエルの原文です--------------------- ------ ----------------------------------
元リンク(http ://www.yuansir-web.com/2014/03/20)
まず、SomeComponent という名前のコンポーネントを開発するとします。データベース接続がこのコンポーネントに挿入されます。この例では、データベース接続はコンポーネント内で作成されますが、これを行うと、データベース接続パラメータやデータベース タイプなどの一部のパラメータを変更できなくなります。
<span style="color: #008080;"> 1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;"> 2</span> <span style="color: #008080;"> 3</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> SomeComponent</span><span style="color: #008080;"> 4</span> <span style="color: #000000;">{</span><span style="color: #008080;"> 5</span> <span style="color: #008080;"> 6</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*</span><span style="color: #008080;"> 7</span> <span style="color: #008000;"> * The instantiation of the connection is hardcoded inside</span><span style="color: #008080;"> 8</span> <span style="color: #008000;"> * the component so is difficult to replace it externally</span><span style="color: #008080;"> 9</span> <span style="color: #008000;"> * or change its behavior</span><span style="color: #008080;">10</span> <span style="color: #008000;">*/</span><span style="color: #008080;">11</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> someDbTask()</span><span style="color: #008080;">12</span> <span style="color: #000000;"> {</span><span style="color: #008080;">13</span> <span style="color: #800080;">$connection</span> = <span style="color: #0000ff;">new</span> Connection(<span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #008080;">14</span> "host" => "localhost",<span style="color: #008080;">15</span> "username" => "root",<span style="color: #008080;">16</span> "password" => "secret",<span style="color: #008080;">17</span> "dbname" => "invo"<span style="color: #008080;">18</span> <span style="color: #000000;"> ));</span><span style="color: #008080;">19</span> <span style="color: #008080;">20</span> <span style="color: #008000;">//</span><span style="color: #008000;"> ...</span><span style="color: #008080;">21</span> <span style="color: #000000;"> }</span><span style="color: #008080;">22</span> <span style="color: #008080;">23</span> <span style="color: #000000;">}</span><span style="color: #008080;">24</span> <span style="color: #008080;">25</span> <span style="color: #800080;">$some</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> SomeComponent();</span><span style="color: #008080;">26</span> <span style="color: #800080;">$some</span>->someDbTask();
上記の問題を解決するには、使用前に外部接続を作成し、それをコンテナーに注入する必要があります。今のところ、これは良い解決策のように見えます:
<span style="color: #008080;"> 1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;"> 2</span> <span style="color: #008080;"> 3</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> SomeComponent</span><span style="color: #008080;"> 4</span> <span style="color: #000000;">{</span><span style="color: #008080;"> 5</span> <span style="color: #008080;"> 6</span> <span style="color: #0000ff;">protected</span> <span style="color: #800080;">$_connection</span><span style="color: #000000;">;</span><span style="color: #008080;"> 7</span> <span style="color: #008080;"> 8</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*</span><span style="color: #008080;"> 9</span> <span style="color: #008000;"> * Sets the connection externally</span><span style="color: #008080;">10</span> <span style="color: #008000;">*/</span><span style="color: #008080;">11</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> setConnection(<span style="color: #800080;">$connection</span><span style="color: #000000;">)</span><span style="color: #008080;">12</span> <span style="color: #000000;"> {</span><span style="color: #008080;">13</span> <span style="color: #800080;">$this</span>->_connection = <span style="color: #800080;">$connection</span><span style="color: #000000;">;</span><span style="color: #008080;">14</span> <span style="color: #000000;"> }</span><span style="color: #008080;">15</span> <span style="color: #008080;">16</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> someDbTask()</span><span style="color: #008080;">17</span> <span style="color: #000000;"> {</span><span style="color: #008080;">18</span> <span style="color: #800080;">$connection</span> = <span style="color: #800080;">$this</span>-><span style="color: #000000;">_connection;</span><span style="color: #008080;">19</span> <span style="color: #008080;">20</span> <span style="color: #008000;">//</span><span style="color: #008000;"> ...</span><span style="color: #008080;">21</span> <span style="color: #000000;"> }</span><span style="color: #008080;">22</span> <span style="color: #008080;">23</span> <span style="color: #000000;">}</span><span style="color: #008080;">24</span> <span style="color: #008080;">25</span> <span style="color: #800080;">$some</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> SomeComponent();</span><span style="color: #008080;">26</span> <span style="color: #008080;">27</span> <span style="color: #008000;">//</span><span style="color: #008000;">Create the connection</span><span style="color: #008080;">28</span> <span style="color: #800080;">$connection</span> = <span style="color: #0000ff;">new</span> Connection(<span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #008080;">29</span> "host" => "localhost",<span style="color: #008080;">30</span> "username" => "root",<span style="color: #008080;">31</span> "password" => "secret",<span style="color: #008080;">32</span> "dbname" => "invo"<span style="color: #008080;">33</span> <span style="color: #000000;">));</span><span style="color: #008080;">34</span> <span style="color: #008080;">35</span> <span style="color: #008000;">//</span><span style="color: #008000;">Inject the connection in the component</span><span style="color: #008080;">36</span> <span style="color: #800080;">$some</span>->setConnection(<span style="color: #800080;">$connection</span><span style="color: #000000;">);</span><span style="color: #008080;">37</span> <span style="color: #008080;">38</span> <span style="color: #800080;">$some</span>->someDbTask();
次に、このコンポーネントをアプリケーション内のさまざまな場所で使用する問題を考えてみましょう。データベース接続が複数回作成されます。 。データベース接続インスタンスを使用後に作成するのではなく、グローバル レジストリと同様の方法を使用して、ここからデータベース接続インスタンスを取得します。
<span style="color: #008080;"> 1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;"> 2</span> <span style="color: #008080;"> 3</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> Registry</span><span style="color: #008080;"> 4</span> <span style="color: #000000;">{</span><span style="color: #008080;"> 5</span> <span style="color: #008080;"> 6</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*</span><span style="color: #008080;"> 7</span> <span style="color: #008000;"> * Returns the connection</span><span style="color: #008080;"> 8</span> <span style="color: #008000;">*/</span><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> getConnection()</span><span style="color: #008080;">10</span> <span style="color: #000000;"> {</span><span style="color: #008080;">11</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span> Connection(<span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #008080;">12</span> "host" => "localhost",<span style="color: #008080;">13</span> "username" => "root",<span style="color: #008080;">14</span> "password" => "secret",<span style="color: #008080;">15</span> "dbname" => "invo"<span style="color: #008080;">16</span> <span style="color: #000000;"> ));</span><span style="color: #008080;">17</span> <span style="color: #000000;"> }</span><span style="color: #008080;">18</span> <span style="color: #008080;">19</span> <span style="color: #000000;">}</span><span style="color: #008080;">20</span> <span style="color: #008080;">21</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> SomeComponent</span><span style="color: #008080;">22</span> <span style="color: #000000;">{</span><span style="color: #008080;">23</span> <span style="color: #008080;">24</span> <span style="color: #0000ff;">protected</span> <span style="color: #800080;">$_connection</span><span style="color: #000000;">;</span><span style="color: #008080;">25</span> <span style="color: #008080;">26</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*</span><span style="color: #008080;">27</span> <span style="color: #008000;"> * Sets the connection externally</span><span style="color: #008080;">28</span> <span style="color: #008000;">*/</span><span style="color: #008080;">29</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> setConnection(<span style="color: #800080;">$connection</span><span style="color: #000000;">){</span><span style="color: #008080;">30</span> <span style="color: #800080;">$this</span>->_connection = <span style="color: #800080;">$connection</span><span style="color: #000000;">;</span><span style="color: #008080;">31</span> <span style="color: #000000;"> }</span><span style="color: #008080;">32</span> <span style="color: #008080;">33</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> someDbTask()</span><span style="color: #008080;">34</span> <span style="color: #000000;"> {</span><span style="color: #008080;">35</span> <span style="color: #800080;">$connection</span> = <span style="color: #800080;">$this</span>-><span style="color: #000000;">_connection;</span><span style="color: #008080;">36</span> <span style="color: #008080;">37</span> <span style="color: #008000;">//</span><span style="color: #008000;"> ...</span><span style="color: #008080;">38</span> <span style="color: #000000;"> }</span><span style="color: #008080;">39</span> <span style="color: #008080;">40</span> <span style="color: #000000;">}</span><span style="color: #008080;">41</span> <span style="color: #008080;">42</span> <span style="color: #800080;">$some</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> SomeComponent();</span><span style="color: #008080;">43</span> <span style="color: #008080;">44</span> <span style="color: #008000;">//</span><span style="color: #008000;">Pass the connection defined in the registry</span><span style="color: #008080;">45</span> <span style="color: #800080;">$some</span>->setConnection(Registry::<span style="color: #000000;">getConnection());</span><span style="color: #008080;">46</span> <span style="color: #008080;">47</span> <span style="color: #800080;">$some</span>->someDbTask();
さて、コンポーネントに 2 つのメソッドを実装する必要があると想像してみましょう。最初に新しいデータベース接続を作成する必要があり、2 番目のメソッドは常に共有接続を取得します。
<span style="color: #008080;"> 1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;"> 2</span> <span style="color: #008080;"> 3</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> Registry</span><span style="color: #008080;"> 4</span> <span style="color: #000000;">{</span><span style="color: #008080;"> 5</span> <span style="color: #008080;"> 6</span> <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">static</span> <span style="color: #800080;">$_connection</span><span style="color: #000000;">;</span><span style="color: #008080;"> 7</span> <span style="color: #008080;"> 8</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*</span><span style="color: #008080;"> 9</span> <span style="color: #008000;"> * Creates a connection</span><span style="color: #008080;">10</span> <span style="color: #008000;">*/</span><span style="color: #008080;">11</span> <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> _createConnection()</span><span style="color: #008080;">12</span> <span style="color: #000000;"> {</span><span style="color: #008080;">13</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span> Connection(<span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #008080;">14</span> "host" => "localhost",<span style="color: #008080;">15</span> "username" => "root",<span style="color: #008080;">16</span> "password" => "secret",<span style="color: #008080;">17</span> "dbname" => "invo"<span style="color: #008080;">18</span> <span style="color: #000000;"> ));</span><span style="color: #008080;">19</span> <span style="color: #000000;"> }</span><span style="color: #008080;">20</span> <span style="color: #008080;">21</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*</span><span style="color: #008080;">22</span> <span style="color: #008000;"> * Creates a connection only once and returns it</span><span style="color: #008080;">23</span> <span style="color: #008000;">*/</span><span style="color: #008080;">24</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> getSharedConnection()</span><span style="color: #008080;">25</span> <span style="color: #000000;"> {</span><span style="color: #008080;">26</span> <span style="color: #0000ff;">if</span> (self::<span style="color: #800080;">$_connection</span>===<span style="color: #0000ff;">null</span><span style="color: #000000;">){</span><span style="color: #008080;">27</span> <span style="color: #800080;">$connection</span> = self::<span style="color: #000000;">_createConnection();</span><span style="color: #008080;">28</span> self::<span style="color: #800080;">$_connection</span> = <span style="color: #800080;">$connection</span><span style="color: #000000;">;</span><span style="color: #008080;">29</span> <span style="color: #000000;"> }</span><span style="color: #008080;">30</span> <span style="color: #0000ff;">return</span> self::<span style="color: #800080;">$_connection</span><span style="color: #000000;">;</span><span style="color: #008080;">31</span> <span style="color: #000000;"> }</span><span style="color: #008080;">32</span> <span style="color: #008080;">33</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*</span><span style="color: #008080;">34</span> <span style="color: #008000;"> * Always returns a new connection</span><span style="color: #008080;">35</span> <span style="color: #008000;">*/</span><span style="color: #008080;">36</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> getNewConnection()</span><span style="color: #008080;">37</span> <span style="color: #000000;"> {</span><span style="color: #008080;">38</span> <span style="color: #0000ff;">return</span> self::<span style="color: #000000;">_createConnection();</span><span style="color: #008080;">39</span> <span style="color: #000000;"> }</span><span style="color: #008080;">40</span> <span style="color: #008080;">41</span> <span style="color: #000000;">}</span><span style="color: #008080;">42</span> <span style="color: #008080;">43</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> SomeComponent</span><span style="color: #008080;">44</span> <span style="color: #000000;">{</span><span style="color: #008080;">45</span> <span style="color: #008080;">46</span> <span style="color: #0000ff;">protected</span> <span style="color: #800080;">$_connection</span><span style="color: #000000;">;</span><span style="color: #008080;">47</span> <span style="color: #008080;">48</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*</span><span style="color: #008080;">49</span> <span style="color: #008000;"> * Sets the connection externally</span><span style="color: #008080;">50</span> <span style="color: #008000;">*/</span><span style="color: #008080;">51</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> setConnection(<span style="color: #800080;">$connection</span><span style="color: #000000;">){</span><span style="color: #008080;">52</span> <span style="color: #800080;">$this</span>->_connection = <span style="color: #800080;">$connection</span><span style="color: #000000;">;</span><span style="color: #008080;">53</span> <span style="color: #000000;"> }</span><span style="color: #008080;">54</span> <span style="color: #008080;">55</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*</span><span style="color: #008080;">56</span> <span style="color: #008000;"> * This method always needs the shared connection</span><span style="color: #008080;">57</span> <span style="color: #008000;">*/</span><span style="color: #008080;">58</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> someDbTask()</span><span style="color: #008080;">59</span> <span style="color: #000000;"> {</span><span style="color: #008080;">60</span> <span style="color: #800080;">$connection</span> = <span style="color: #800080;">$this</span>-><span style="color: #000000;">_connection;</span><span style="color: #008080;">61</span> <span style="color: #008080;">62</span> <span style="color: #008000;">//</span><span style="color: #008000;"> ...</span><span style="color: #008080;">63</span> <span style="color: #000000;"> }</span><span style="color: #008080;">64</span> <span style="color: #008080;">65</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*</span><span style="color: #008080;">66</span> <span style="color: #008000;"> * This method always needs a new connection</span><span style="color: #008080;">67</span> <span style="color: #008000;">*/</span><span style="color: #008080;">68</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> someOtherDbTask(<span style="color: #800080;">$connection</span><span style="color: #000000;">)</span><span style="color: #008080;">69</span> <span style="color: #000000;"> {</span><span style="color: #008080;">70</span> <span style="color: #008080;">71</span> <span style="color: #000000;"> }</span><span style="color: #008080;">72</span> <span style="color: #008080;">73</span> <span style="color: #000000;">}</span><span style="color: #008080;">74</span> <span style="color: #008080;">75</span> <span style="color: #800080;">$some</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> SomeComponent();</span><span style="color: #008080;">76</span> <span style="color: #008080;">77</span> <span style="color: #008000;">//</span><span style="color: #008000;">This injects the shared connection</span><span style="color: #008080;">78</span> <span style="color: #800080;">$some</span>->setConnection(Registry::<span style="color: #000000;">getSharedConnection());</span><span style="color: #008080;">79</span> <span style="color: #008080;">80</span> <span style="color: #800080;">$some</span>-><span style="color: #000000;">someDbTask();</span><span style="color: #008080;">81</span> <span style="color: #008080;">82</span> <span style="color: #008000;">//</span><span style="color: #008000;">Here, we always pass a new connection as parameter</span><span style="color: #008080;">83</span> <span style="color: #800080;">$some</span>->someOtherDbTask(Registry::getConnection());
これまで、依存関係注入を使用して問題を解決する方法を見てきました。コード内に依存関係を作成する代わりに、依存関係をパラメーターとして渡します。これにより、プログラムの保守が容易になり、プログラム コードの結合が減少し、一種の疎結合が実現します。しかし、長期的には、この形式の依存性注入にはいくつかの欠点もあります。
たとえば、コンポーネントに多くの依存関係がある場合、渡すセッター メソッドを複数作成するか、渡すコンストラクターを作成する必要があります。さらに、コンポーネントを使用するたびに依存コンポーネントを作成する必要があるため、コードのメンテナンスは次のようになります。
<span style="color: #008080;"> 1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;"> 2</span> <span style="color: #008080;"> 3</span> <span style="color: #008000;">//</span><span style="color: #008000;">Create the dependencies or retrieve them from the registry</span><span style="color: #008080;"> 4</span> <span style="color: #800080;">$connection</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Connection();</span><span style="color: #008080;"> 5</span> <span style="color: #800080;">$session</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Session();</span><span style="color: #008080;"> 6</span> <span style="color: #800080;">$fileSystem</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> FileSystem();</span><span style="color: #008080;"> 7</span> <span style="color: #800080;">$filter</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Filter();</span><span style="color: #008080;"> 8</span> <span style="color: #800080;">$selector</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Selector();</span><span style="color: #008080;"> 9</span> <span style="color: #008080;">10</span> <span style="color: #008000;">//</span><span style="color: #008000;">Pass them as constructor parameters</span><span style="color: #008080;">11</span> <span style="color: #800080;">$some</span> = <span style="color: #0000ff;">new</span> SomeComponent(<span style="color: #800080;">$connection</span>, <span style="color: #800080;">$session</span>, <span style="color: #800080;">$fileSystem</span>, <span style="color: #800080;">$filter</span>, <span style="color: #800080;">$selector</span><span style="color: #000000;">);</span><span style="color: #008080;">12</span> <span style="color: #008080;">13</span> <span style="color: #008000;">//</span><span style="color: #008000;"> ... or using setters</span><span style="color: #008080;">14</span> <span style="color: #008080;">15</span> <span style="color: #800080;">$some</span>->setConnection(<span style="color: #800080;">$connection</span><span style="color: #000000;">);</span><span style="color: #008080;">16</span> <span style="color: #800080;">$some</span>->setSession(<span style="color: #800080;">$session</span><span style="color: #000000;">);</span><span style="color: #008080;">17</span> <span style="color: #800080;">$some</span>->setFileSystem(<span style="color: #800080;">$fileSystem</span><span style="color: #000000;">);</span><span style="color: #008080;">18</span> <span style="color: #800080;">$some</span>->setFilter(<span style="color: #800080;">$filter</span><span style="color: #000000;">);</span><span style="color: #008080;">19</span> <span style="color: #800080;">$some</span>->setSelector(<span style="color: #800080;">$selector</span>);
Iこのオブジェクトはアプリケーション内のさまざまな場所で作成されます。依存コンポーネントが必要ない場合は、コード挿入部分に移動して、コンストラクターまたはセッター メソッドのパラメーターを削除する必要があります。この問題を解決するには、もう一度グローバル レジストリを使用してコンポーネントを作成します。ただし、オブジェクトを作成する前に、新しい抽象化レイヤーが追加されます:
<span style="color: #008080;"> 1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;"> 2</span> <span style="color: #008080;"> 3</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> SomeComponent</span><span style="color: #008080;"> 4</span> <span style="color: #000000;">{</span><span style="color: #008080;"> 5</span> <span style="color: #008080;"> 6</span> <span style="color: #008000;">//</span><span style="color: #008000;"> ...</span><span style="color: #008080;"> 7</span> <span style="color: #008080;"> 8</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*</span><span style="color: #008080;"> 9</span> <span style="color: #008000;"> * Define a factory method to create SomeComponent instances injecting its dependencies</span><span style="color: #008080;">10</span> <span style="color: #008000;">*/</span><span style="color: #008080;">11</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> factory()</span><span style="color: #008080;">12</span> <span style="color: #000000;"> {</span><span style="color: #008080;">13</span> <span style="color: #008080;">14</span> <span style="color: #800080;">$connection</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Connection();</span><span style="color: #008080;">15</span> <span style="color: #800080;">$session</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Session();</span><span style="color: #008080;">16</span> <span style="color: #800080;">$fileSystem</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> FileSystem();</span><span style="color: #008080;">17</span> <span style="color: #800080;">$filter</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Filter();</span><span style="color: #008080;">18</span> <span style="color: #800080;">$selector</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Selector();</span><span style="color: #008080;">19</span> <span style="color: #008080;">20</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span> self(<span style="color: #800080;">$connection</span>, <span style="color: #800080;">$session</span>, <span style="color: #800080;">$fileSystem</span>, <span style="color: #800080;">$filter</span>, <span style="color: #800080;">$selector</span><span style="color: #000000;">);</span><span style="color: #008080;">21</span> <span style="color: #000000;"> }</span><span style="color: #008080;">22</span> <span style="color: #008080;">23</span> }
現時点では、問題の始まりに戻ったようで、依存関係を作成しています。コンポーネント内では、毎回修正を加えて問題を解決する方法を探していますが、これは良いアプローチではありません。
これらの問題を解決するための実用的かつ洗練された方法は、コンテナの依存関係の注入を使用することです。前に説明したように、コンテナはグローバル レジストリとして機能し、コンテナの依存関係の注入をブリッジとして使用して依存関係を解決します。私たちのコードは結合度が低くなり、コンポーネントの複雑さが大幅に軽減されます。
<span style="color: #008080;"> 1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;"> 2</span> <span style="color: #008080;"> 3</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> SomeComponent</span><span style="color: #008080;"> 4</span> <span style="color: #000000;">{</span><span style="color: #008080;"> 5</span> <span style="color: #008080;"> 6</span> <span style="color: #0000ff;">protected</span> <span style="color: #800080;">$_di</span><span style="color: #000000;">;</span><span style="color: #008080;"> 7</span> <span style="color: #008080;"> 8</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(<span style="color: #800080;">$di</span><span style="color: #000000;">)</span><span style="color: #008080;"> 9</span> <span style="color: #000000;"> {</span><span style="color: #008080;">10</span> <span style="color: #800080;">$this</span>->_di = <span style="color: #800080;">$di</span><span style="color: #000000;">;</span><span style="color: #008080;">11</span> <span style="color: #000000;"> }</span><span style="color: #008080;">12</span> <span style="color: #008080;">13</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> someDbTask()</span><span style="color: #008080;">14</span> <span style="color: #000000;"> {</span><span style="color: #008080;">15</span> <span style="color: #008080;">16</span> <span style="color: #008000;">//</span><span style="color: #008000;"> Get the connection service</span><span style="color: #008080;">17</span> <span style="color: #008000;"> // Always returns a new connection</span><span style="color: #008080;">18</span> <span style="color: #800080;">$connection</span> = <span style="color: #800080;">$this</span>->_di->get('db'<span style="color: #000000;">);</span><span style="color: #008080;">19</span> <span style="color: #008080;">20</span> <span style="color: #000000;"> }</span><span style="color: #008080;">21</span> <span style="color: #008080;">22</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> someOtherDbTask()</span><span style="color: #008080;">23</span> <span style="color: #000000;"> {</span><span style="color: #008080;">24</span> <span style="color: #008080;">25</span> <span style="color: #008000;">//</span><span style="color: #008000;"> Get a shared connection service,</span><span style="color: #008080;">26</span> <span style="color: #008000;"> // this will return the same connection everytime</span><span style="color: #008080;">27</span> <span style="color: #800080;">$connection</span> = <span style="color: #800080;">$this</span>->_di->getShared('db'<span style="color: #000000;">);</span><span style="color: #008080;">28</span> <span style="color: #008080;">29</span> <span style="color: #008000;">//</span><span style="color: #008000;">This method also requires a input filtering service</span><span style="color: #008080;">30</span> <span style="color: #800080;">$filter</span> = <span style="color: #800080;">$this</span>->_db->get('filter'<span style="color: #000000;">);</span><span style="color: #008080;">31</span> <span style="color: #008080;">32</span> <span style="color: #000000;"> }</span><span style="color: #008080;">33</span> <span style="color: #008080;">34</span> <span style="color: #000000;">}</span><span style="color: #008080;">35</span> <span style="color: #008080;">36</span> <span style="color: #800080;">$di</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Phalcon\DI();</span><span style="color: #008080;">37</span> <span style="color: #008080;">38</span> <span style="color: #008000;">//</span><span style="color: #008000;">Register a "db" service in the container</span><span style="color: #008080;">39</span> <span style="color: #800080;">$di</span>->set('db', <span style="color: #0000ff;">function</span><span style="color: #000000;">(){</span><span style="color: #008080;">40</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span> Connection(<span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #008080;">41</span> "host" => "localhost",<span style="color: #008080;">42</span> "username" => "root",<span style="color: #008080;">43</span> "password" => "secret",<span style="color: #008080;">44</span> "dbname" => "invo"<span style="color: #008080;">45</span> <span style="color: #000000;"> ));</span><span style="color: #008080;">46</span> <span style="color: #000000;">});</span><span style="color: #008080;">47</span> <span style="color: #008080;">48</span> <span style="color: #008000;">//</span><span style="color: #008000;">Register a "filter" service in the container</span><span style="color: #008080;">49</span> <span style="color: #800080;">$di</span>->set('filter', <span style="color: #0000ff;">function</span><span style="color: #000000;">(){</span><span style="color: #008080;">50</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> Filter();</span><span style="color: #008080;">51</span> <span style="color: #000000;">});</span><span style="color: #008080;">52</span> <span style="color: #008080;">53</span> <span style="color: #008000;">//</span><span style="color: #008000;">Register a "session" service in the container</span><span style="color: #008080;">54</span> <span style="color: #800080;">$di</span>->set('session', <span style="color: #0000ff;">function</span><span style="color: #000000;">(){</span><span style="color: #008080;">55</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> Session();</span><span style="color: #008080;">56</span> <span style="color: #000000;">});</span><span style="color: #008080;">57</span> <span style="color: #008080;">58</span> <span style="color: #008000;">//</span><span style="color: #008000;">Pass the service container as unique parameter</span><span style="color: #008080;">59</span> <span style="color: #800080;">$some</span> = <span style="color: #0000ff;">new</span> SomeComponent(<span style="color: #800080;">$di</span><span style="color: #000000;">);</span><span style="color: #008080;">60</span> <span style="color: #008080;">61</span> <span style="color: #800080;">$some</span>->someTask();
これで、コンポーネントは特定のサービスにアクセスするときにのみ必要となり、必要ない場合は必要になります。リソースを節約するための初期化も行われていません。コンポーネントは高度に分離されています。コンポーネントの動作やその他の側面は、コンポーネント自体には影響しません。
今回の実装方法¶
PhalconDIはサービスの依存性注入機能を実装するコンポーネントであり、それ自体がコンテナでもあります。
Phalcon は高度に分離されているため、PhalconDI は他のコンポーネントを統合するために使用されるフレームワークの重要な部分であり、開発者はこのコンポーネントを使用して、アプリケーション内のさまざまなクラス ファイルのインスタンスを依存関係に挿入したり管理したりすることもできます。
基本的に、このコンポーネントは制御の反転パターンを実装します。これに基づいて、オブジェクトはコンストラクターでパラメーターを受け取ったり、セッターを使用してインジェクションを実装するのではなく、サービスの依存関係インジェクションを直接要求します。コンポーネントの必要な依存関係を取得する方法が 1 つしかないため、これによりプログラム全体の複雑さが大幅に軽減されます。
さらに、このパターンによりコードのテスト容易性が向上し、エラーが発生しにくくなります。
コンテナへのサービスの登録¶
サービスは、フレームワーク自体または開発者によって登録できます。コンポーネント A がコンポーネント B (またはそのクラスのインスタンス) への呼び出しをリクエストする場合、コンポーネント B のインスタンスを作成する代わりに、コンテナからコンポーネント B への呼び出しをリクエストできます。
この働き方には多くの利点があります:
我们可以更换一个组件,从他们本身或者第三方轻松创建。
在组件发布之前,我们可以充分的控制对象的初始化,并对对象进行各种设置。
我们可以使用统一的方式从组件得到一个结构化的全局实例
服务可以通过以下几种方式注入到容器:
<span style="color: #008080;"> 1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;"> 2</span> <span style="color: #008080;"> 3</span> <span style="color: #008000;">//</span><span style="color: #008000;">Create the Dependency Injector Container</span><span style="color: #008080;"> 4</span> <span style="color: #800080;">$di</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Phalcon\DI();</span><span style="color: #008080;"> 5</span> <span style="color: #008080;"> 6</span> <span style="color: #008000;">//</span><span style="color: #008000;">By its class name</span><span style="color: #008080;"> 7</span> <span style="color: #800080;">$di</span>->set("request", 'Phalcon\Http\Request'<span style="color: #000000;">);</span><span style="color: #008080;"> 8</span> <span style="color: #008080;"> 9</span> <span style="color: #008000;">//</span><span style="color: #008000;">Using an anonymous function, the instance will lazy loaded</span><span style="color: #008080;">10</span> <span style="color: #800080;">$di</span>->set("request", <span style="color: #0000ff;">function</span><span style="color: #000000;">(){</span><span style="color: #008080;">11</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> Phalcon\Http\Request();</span><span style="color: #008080;">12</span> <span style="color: #000000;">});</span><span style="color: #008080;">13</span> <span style="color: #008080;">14</span> <span style="color: #008000;">//</span><span style="color: #008000;">Registering directly an instance</span><span style="color: #008080;">15</span> <span style="color: #800080;">$di</span>->set("request", <span style="color: #0000ff;">new</span><span style="color: #000000;"> Phalcon\Http\Request());</span><span style="color: #008080;">16</span> <span style="color: #008080;">17</span> <span style="color: #008000;">//</span><span style="color: #008000;">Using an array definition</span><span style="color: #008080;">18</span> <span style="color: #800080;">$di</span>->set("request", <span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #008080;">19</span> "className" => 'Phalcon\Http\Request'<span style="color: #008080;">20</span> ));
在上面的例子中,当向框架请求访问一个请求数据时,它将首先确定容器中是否存在这个”reqeust”名称的服务。
容器会反回一个请求数据的实例,开发人员最终得到他们想要的组件。
在上面示例中的每一种方法都有优缺点,具体使用哪一种,由开发过程中的特定场景来决定的。
用一个字符串来设定一个服务非常简单,但缺少灵活性。设置服务时,使用数组则提供了更多的灵活性,而且可以使用较复杂的代码。lambda函数是两者之间一个很好的平衡,但也可能导致更多的维护管理成本。
Phalcon\DI 提供服务的延迟加载。除非开发人员在注入服务的时候直接实例化一个对象,然后存存储到容器中。在容器中,通过数组,字符串等方式存储的服务都将被延迟加载,即只有在请求对象的时候才被初始化。
<span style="color: #008080;"> 1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;"> 2</span> <span style="color: #008080;"> 3</span> <span style="color: #008000;">//</span><span style="color: #008000;">Register a service "db" with a class name and its parameters</span><span style="color: #008080;"> 4</span> <span style="color: #800080;">$di</span>->set("db", <span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #008080;"> 5</span> "className" => "Phalcon\Db\Adapter\Pdo\Mysql",<span style="color: #008080;"> 6</span> "parameters" => <span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #008080;"> 7</span> "parameter" => <span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #008080;"> 8</span> "host" => "localhost",<span style="color: #008080;"> 9</span> "username" => "root",<span style="color: #008080;">10</span> "password" => "secret",<span style="color: #008080;">11</span> "dbname" => "blog"<span style="color: #008080;">12</span> <span style="color: #000000;"> )</span><span style="color: #008080;">13</span> <span style="color: #000000;"> )</span><span style="color: #008080;">14</span> <span style="color: #000000;">));</span><span style="color: #008080;">15</span> <span style="color: #008080;">16</span> <span style="color: #008000;">//</span><span style="color: #008000;">Using an anonymous function</span><span style="color: #008080;">17</span> <span style="color: #800080;">$di</span>->set("db", <span style="color: #0000ff;">function</span><span style="color: #000000;">(){</span><span style="color: #008080;">18</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span> Phalcon\Db\Adapter\Pdo\<span style="color: #008080;">Mysql</span>(<span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #008080;">19</span> "host" => "localhost",<span style="color: #008080;">20</span> "username" => "root",<span style="color: #008080;">21</span> "password" => "secret",<span style="color: #008080;">22</span> "dbname" => "blog"<span style="color: #008080;">23</span> <span style="color: #000000;"> ));</span><span style="color: #008080;">24</span> });
以上这两种服务的注册方式产生相同的结果。然后,通过数组定义的,在后面需要的时候,你可以修改服务参数:
<span style="color: #008080;">1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;">2</span> <span style="color: #008080;">3</span> <span style="color: #800080;">$di</span>->setParameter("db", 0, <span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #008080;">4</span> "host" => "localhost",<span style="color: #008080;">5</span> "username" => "root",<span style="color: #008080;">6</span> "password" => "secret"<span style="color: #008080;">7</span> ));
从容器中获得服务的最简单方式就是使用”get”方法,它将从容器中返回一个新的实例:
<span style="color: #008080;">1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;">2</span> <span style="color: #800080;">$request</span> = <span style="color: #800080;">$di</span>->get("request");
或者通过下面这种魔术方法的形式调用:
<span style="color: #008080;">1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;">2</span> <span style="color: #008080;">3</span> <span style="color: #800080;">$request</span> = <span style="color: #800080;">$di</span>-><span style="color: #000000;">getRequest();</span><span style="color: #008080;">4</span> <span style="color: #008080;">5</span> Phalcon\DI 同时允许服务重用,为了得到一个已经实例化过的服务,可以使用 getShared() 方法的形式来获得服务。
具体的 Phalcon\Http\Request 请求示例:
<span style="color: #008080;">1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;">2</span> <span style="color: #008080;">3</span> <span style="color: #800080;">$request</span> = <span style="color: #800080;">$di</span>->getShared("request");
参数还可以在请求的时候通过将一个数组参数传递给构造函数的方式:
<span style="color: #008080;">1</span> <?<span style="color: #000000;">php</span><span style="color: #008080;">2</span> <span style="color: #008080;">3</span> <span style="color: #800080;">$component</span> = <span style="color: #800080;">$di</span>->get("MyComponent", <span style="color: #0000ff;">array</span>("some-parameter", "other"))