PHP の依存関係の注入、ロード順序は考慮されなくなりました

WBOY
リリース: 2016-06-13 12:28:25
オリジナル
724 人が閲覧しました

PHP の依存関係の注入、読み込み順序を考慮しなくなりました

このトピックについて話す前に、より高度なアイデアについて話しましょう - 「依存関係反転の原則

「依存関係の反転」従来のソフトウェアでは、上位層のコードが下位層のコードに依存しており、下位層のコードが変更されると、それに応じて上位層のコードも変更する必要があるため、メンテナンスコストが高くなります。依存関係逆転の原則の考え方は、上位層が下位層とインターフェイスに依存しないということです。つまり、上位層のコードがインターフェイスを定義し、下位層のコードがインターフェイスを実装します。下位層が上位層のインターフェイスに依存するため、結合が減少し、システムの柔軟性が向上します。"

上 説明は少し曖昧です。この理論を以下の実際のコードで説明しましょう。

たとえば、ユーザー登録が完了したらメールを送信する必要がある場合、次のようなコードになります。

最初にメールクラス 'Email.class.php' があります。 '

<span style="color: #0000ff;">class</span> <span style="color: #008080;">Mail</span><span style="color: #000000;">{    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> send()    {        </span><span style="color: #008000;">/*</span><span style="color: #008000;">这里是如何发送邮件的代码</span><span style="color: #008000;">*/</span><span style="color: #000000;">    }}</span>
ログイン後にコピー

クラス 'Register.class.php' を登録しました

<span style="color: #0000ff;">class</span><span style="color: #000000;"> Register{    </span><span style="color: #0000ff;">private</span> <span style="color: #800080;">$_emailObj</span><span style="color: #000000;">;    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> doRegister()    {        </span><span style="color: #008000;">/*</span><span style="color: #008000;">这里是如何注册</span><span style="color: #008000;">*/</span>        <span style="color: #800080;">$this</span>->_emailObj = <span style="color: #0000ff;">new</span> <span style="color: #008080;">Mail</span><span style="color: #000000;">();        </span><span style="color: #800080;">$this</span>->_emailObj->send();<span style="color: #008000;">//</span><span style="color: #008000;">发送邮件</span><span style="color: #000000;">    }}</span>
ログイン後にコピー

次に登録を開始します

<span style="color: #0000ff;">include</span> 'Mail.class.php'<span style="color: #000000;">;</span><span style="color: #0000ff;">include</span> 'Register.class.php'<span style="color: #000000;">;</span><span style="color: #800080;">$reg</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Register();</span><span style="color: #800080;">$reg</span>->doRegister();
ログイン後にコピー

この機能をすぐにオンラインにすると、すべてがうまくいくようです...xxx 日後に、製品担当者がメールの調子が悪いので送信するように言いました。テキスト メッセージを送信するものを使用する必要がありました。それから、簡単だと言われたので、[メール] カテゴリを変更しました...

数日後、製品スタッフは、テキスト メッセージの送信にコストがかかりすぎると言いました。高いのでメールに変更しました...今この瞬間、私の心の中で一万頭の草泥馬が駆け抜けています...

製品の犬にはよくあることですが、花は散ってしまいます...

上記のシナリオの問題は、毎回 'Mail' クラスを変更する必要があり、コードの再利用性が非常に低く、高レベルであることです。最下層に依存しすぎている。次に、最下位層が上位層によって定式化されたインターフェイスを継承し、上位層がそのインターフェイスに依存する「依存関係逆転原理」を検討します。

<span style="color: #0000ff;">interface</span> <span style="color: #008080;">Mail</span><span style="color: #000000;">{    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> send();}</span>
ログイン後にコピー
<span style="color: #0000ff;">class</span> Email <span style="color: #0000ff;">implements</span> <span style="color: #008080;">Mail</span><span style="color: #000000;">(){    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> send()    {        </span><span style="color: #008000;">//</span><span style="color: #008000;">发送Email</span><span style="color: #000000;">    }}</span>
ログイン後にコピー
<span style="color: #0000ff;">class</span> SmsMail <span style="color: #0000ff;">implements</span> <span style="color: #008080;">Mail</span><span style="color: #000000;">(){    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> send()    {        </span><span style="color: #008000;">//</span><span style="color: #008000;">发送短信</span><span style="color: #000000;">    }}</span>
ログイン後にコピー
<span style="color: #0000ff;">class</span><span style="color: #000000;"> Register{    </span><span style="color: #0000ff;">private</span> <span style="color: #800080;">$_mailObj</span><span style="color: #000000;">;    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(<span style="color: #008080;">Mail</span> <span style="color: #800080;">$mailObj</span><span style="color: #000000;">)    {        </span><span style="color: #800080;">$this</span>->_mailObj = <span style="color: #800080;">$mailObj</span><span style="color: #000000;">;    }    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> doRegister()    {        </span><span style="color: #008000;">/*</span><span style="color: #008000;">这里是如何注册</span><span style="color: #008000;">*/</span>        <span style="color: #800080;">$this</span>->_mailObj->send();<span style="color: #008000;">//</span><span style="color: #008000;">发送信息</span><span style="color: #000000;">    }}</span>
ログイン後にコピー

以下のメッセージの送信を開始します

<span style="color: #008000;">/*</span><span style="color: #008000;"> 此处省略若干行 </span><span style="color: #008000;">*/</span><span style="color: #800080;">$reg</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Register();</span><span style="color: #800080;">$emailObj</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Email();</span><span style="color: #800080;">$smsObj</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> SmsMail();</span><span style="color: #800080;">$reg</span>->doRegister(<span style="color: #800080;">$emailObj</span>);<span style="color: #008000;">//</span><span style="color: #008000;">使用email发送</span><span style="color: #800080;">$reg</span>->doRegister(<span style="color: #800080;">$smsObj</span>);<span style="color: #008000;">//</span><span style="color: #008000;">使用短信发送</span><span style="color: #008000;">/*</span><span style="color: #008000;"> 你甚至可以发完邮件再发短信 </span><span style="color: #008000;">*/</span>
ログイン後にコピー

上記のコードは、コンストラクター注入メソッドを使用して、メッセージ送信クラスへの 'Register' の依存関係を解決します。これにより、次の条件を満たす限り、テキスト メッセージを送信するためのインターフェイスのみに依存します。送信方法に関係なく、「send」メソッドを使用します。上記の例では、「inject」というアイデアを使用しています。これは、あるクラスのインスタンスを別のクラスのインスタンスに注入する注射器のようなものです。もちろん、「依存関係逆転の原則」は常に実装されています。 「Inject」は、コンストラクターを介して注入するだけでなく、属性注入によっても注入できます。上記では、「setter」を介して「mailObj」属性に値を動的に割り当てることができます。

私は上でたくさん読みましたが、注意深い読者はタイトルに「ロード順序を考慮しなくなりました」という言葉が含まれていることに気づくかもしれません。まだ考慮する必要があります。上記は、最初に情報送信クラスを導入し、次に登録クラスを導入し、それからインスタンス化する必要があるのではないでしょうか? カテゴリが多すぎると、やはり目まいがしてしまいます。

実際、そのようなケースはたくさんあります。最初はそれほど多くのクラスがありませんでしたが、徐々に関数が増え、これを使用する人も増えてきました。クラスをインポートするには、まずそのクラスをインポートし、順序が正しいことを確認する必要があります。 「a は b に依存し、b は c に依存し、c は d に依存し、d は e に依存する」という例があります。「a」のインスタンスを取得するには、「e、d、c、b」を導入する必要があります。インスタンス化の場合、昔の従業員はこの落とし穴を知っていて、それを飛び越えました。ある日、新しい人が入ってきて、「a」をインスタンス化しようとしましたが、エラーが発生し続け、この時点では「a」のビジネス ロジックを確認することしかできませんでした。彼は、最初に 'b' のインスタンスを取得する必要があることを知っていて、次に 'b' のビジネス ロジックを調べました。そして... 1 日経っても、まだ 'a' のインスタンスを取得できませんでした。とリーダーが来て…

では これは新人の技術力の低さなのか、当時の建築家のレベルの低さなのか?

次に、ロード順序を考慮せずに実装する方法のトピックに移りましょう。実装する前に、ロード順序を考慮しないということは、プログラムが自動的にロードされてインスタンス化されることを意味することを理解する必要があります。自動的に。クラスをインスタンス化するには、必要なすべてのパラメーターが '__construct' 関数に渡されていることを確認してください。クラス内の他のクラスを参照する場合は、それらをコンストラクターにも挿入する必要があります。そうしないと、呼び出し時にエラーが発生します。次に、クラスのインスタンス化に必要なパラメータ、他の依存クラスまたはオブジェクト、インスタンス化後の各クラスへの参照を保存するクラスが必要です。

クラスの名前は box 'Container.class.php'、コンテンツは次のとおりです。

<span style="color: #008000;">/*</span><span style="color: #008000;">**    依赖注入类</span><span style="color: #008000;">*/</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Container{    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    [email protected] array 存储各个类的定义  以类的名称为键    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">private</span> <span style="color: #800080;">$_definitions</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">();    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    [email protected] array 存储各个类实例化需要的参数 以类的名称为键    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">private</span> <span style="color: #800080;">$_params</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">();    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    [email protected] array 存储各个类实例化的引用    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">private</span> <span style="color: #800080;">$_reflections</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">();    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    * @var array 各个类依赖的类    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">private</span> <span style="color: #800080;">$_dependencies</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">();    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    * 设置依赖    * @param string $class 类、方法 名称    * @param mixed $defination 类、方法的定义    * @param array $params 类、方法初始化需要的参数    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> set(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$defination</span> = <span style="color: #0000ff;">array</span>(), <span style="color: #800080;">$params</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">())    {        </span><span style="color: #800080;">$this</span>->_params[<span style="color: #800080;">$class</span>] = <span style="color: #800080;">$params</span><span style="color: #000000;">;        </span><span style="color: #800080;">$this</span>->_definitions[<span style="color: #800080;">$class</span>] = <span style="color: #800080;">$this</span>->initDefinition(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$defination</span><span style="color: #000000;">);    }    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    * 获取实例    * @param string $class 类、方法 名称    * @param array $params 实例化需要的参数    * @param array $properties 为实例配置的属性    * @return mixed    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> get(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span> = <span style="color: #0000ff;">array</span>(), <span style="color: #800080;">$properties</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">())    {        </span><span style="color: #0000ff;">if</span>(!<span style="color: #0000ff;">isset</span>(<span style="color: #800080;">$this</span>->_definitions[<span style="color: #800080;">$class</span><span style="color: #000000;">]))        {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果重来没有声明过 则直接创建</span>            <span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span>->bulid(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">);        }        </span><span style="color: #800080;">$defination</span> = <span style="color: #800080;">$this</span>->_definitions[<span style="color: #800080;">$class</span><span style="color: #000000;">];        </span><span style="color: #0000ff;">if</span>(<span style="color: #008080;">is_callable</span>(<span style="color: #800080;">$defination</span>, <span style="color: #0000ff;">true</span><span style="color: #000000;">))        {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果声明是函数</span>            <span style="color: #800080;">$params</span> = <span style="color: #800080;">$this</span>->parseDependencies(<span style="color: #800080;">$this</span>->mergeParams(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span><span style="color: #000000;">));            </span><span style="color: #800080;">$obj</span> = <span style="color: #008080;">call_user_func</span>(<span style="color: #800080;">$defination</span>, <span style="color: #800080;">$this</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">);        }        </span><span style="color: #0000ff;">elseif</span>(<span style="color: #008080;">is_array</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">))        {            </span><span style="color: #800080;">$originalClass</span> = <span style="color: #800080;">$defination</span>['class'<span style="color: #000000;">];            </span><span style="color: #0000ff;">unset</span>(<span style="color: #800080;">$definition</span>['class'<span style="color: #000000;">]);            </span><span style="color: #008000;">//</span><span style="color: #008000;">difinition中除了'class'元素外 其他的都当做实例的属性处理</span>            <span style="color: #800080;">$properties</span> = <span style="color: #008080;">array_merge</span>((<span style="color: #0000ff;">array</span>)<span style="color: #800080;">$definition</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">);            </span><span style="color: #008000;">//</span><span style="color: #008000;">合并该类、函数声明时的参数</span>            <span style="color: #800080;">$params</span> = <span style="color: #800080;">$this</span>->mergeParams(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span><span style="color: #000000;">);            </span><span style="color: #0000ff;">if</span>(<span style="color: #800080;">$originalClass</span> === <span style="color: #800080;">$class</span><span style="color: #000000;">)            {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果声明中的class的名称和关键字的名称相同 则直接生成对象</span>                <span style="color: #800080;">$obj</span> = <span style="color: #800080;">$this</span>->bulid(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">);            }            </span><span style="color: #0000ff;">else</span><span style="color: #000000;">            {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果不同则有可能为别名 则从容器中获取</span>                <span style="color: #800080;">$obj</span> = <span style="color: #800080;">$this</span>->get(<span style="color: #800080;">$originalClass</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">);            }        }        </span><span style="color: #0000ff;">elseif</span>(<span style="color: #008080;">is_object</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">))        {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果是个对象 直接返回</span>            <span style="color: #0000ff;">return</span> <span style="color: #800080;">$defination</span><span style="color: #000000;">;        }        </span><span style="color: #0000ff;">else</span><span style="color: #000000;">        {            </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">Exception</span>(<span style="color: #800080;">$class</span> . ' 声明错误!'<span style="color: #000000;">);        }        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$obj</span><span style="color: #000000;">;    }    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    * 合并参数    * @param string $class 类、函数 名称    * @param array $params 参数    * @return array    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">function</span> mergeParams(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">())    {        </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$this</span>->_params[<span style="color: #800080;">$class</span><span style="color: #000000;">]))        {            </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$params</span><span style="color: #000000;">;        }        </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$params</span><span style="color: #000000;">))        {            </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span>-><span style="color: #000000;">_params;        }        </span><span style="color: #800080;">$result</span> = <span style="color: #800080;">$this</span>->_params[<span style="color: #800080;">$class</span><span style="color: #000000;">];        </span><span style="color: #0000ff;">foreach</span>(<span style="color: #800080;">$params</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$key</span> => <span style="color: #800080;">$value</span><span style="color: #000000;">)         {            </span><span style="color: #800080;">$result</span>[<span style="color: #800080;">$key</span>] = <span style="color: #800080;">$value</span><span style="color: #000000;">;        }        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$result</span><span style="color: #000000;">;    }    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    * 初始化声明    * @param string $class 类、函数 名称    * @param array $defination 类、函数的定义    * @return mixed    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">function</span> initDefinition(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$defination</span><span style="color: #000000;">)    {        </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">))        {            </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">array</span>('class' => <span style="color: #800080;">$class</span><span style="color: #000000;">);        }        </span><span style="color: #0000ff;">if</span>(<span style="color: #008080;">is_string</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">))        {            </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">array</span>('class' => <span style="color: #800080;">$defination</span><span style="color: #000000;">);        }        </span><span style="color: #0000ff;">if</span>(<span style="color: #008080;">is_callable</span>(<span style="color: #800080;">$defination</span>) || <span style="color: #008080;">is_object</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">))        {            </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$defination</span><span style="color: #000000;">;        }        </span><span style="color: #0000ff;">if</span>(<span style="color: #008080;">is_array</span>(<span style="color: #800080;">$defination</span><span style="color: #000000;">))        {            </span><span style="color: #0000ff;">if</span>(!<span style="color: #0000ff;">isset</span>(<span style="color: #800080;">$defination</span>['class'<span style="color: #000000;">]))            {                </span><span style="color: #800080;">$definition</span>['class'] = <span style="color: #800080;">$class</span><span style="color: #000000;">;            }            </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$defination</span><span style="color: #000000;">;        }        </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">Exception</span>(<span style="color: #800080;">$class</span>. ' 声明错误'<span style="color: #000000;">);    }    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    * 创建类实例、函数    * @param string $class 类、函数 名称    * @param array $params 初始化时的参数    * @param array $properties 属性    * @return mixed    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">function</span> bulid(<span style="color: #800080;">$class</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$properties</span><span style="color: #000000;">)    {        </span><span style="color: #0000ff;">list</span>(<span style="color: #800080;">$reflection</span>, <span style="color: #800080;">$dependencies</span>) = <span style="color: #800080;">$this</span>->getDependencies(<span style="color: #800080;">$class</span><span style="color: #000000;">);        </span><span style="color: #0000ff;">foreach</span> ((<span style="color: #0000ff;">array</span>)<span style="color: #800080;">$params</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$index</span> => <span style="color: #800080;">$param</span><span style="color: #000000;">)         {</span><span style="color: #008000;">//</span><span style="color: #008000;">依赖不仅有对象的依赖 还有普通参数的依赖</span>            <span style="color: #800080;">$dependencies</span>[<span style="color: #800080;">$index</span>] = <span style="color: #800080;">$param</span><span style="color: #000000;">;        }        </span><span style="color: #800080;">$dependencies</span> = <span style="color: #800080;">$this</span>->parseDependencies(<span style="color: #800080;">$dependencies</span>, <span style="color: #800080;">$reflection</span><span style="color: #000000;">);        </span><span style="color: #800080;">$obj</span> = <span style="color: #800080;">$reflection</span>->newInstanceArgs(<span style="color: #800080;">$dependencies</span><span style="color: #000000;">);        </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">empty</span>(<span style="color: #800080;">$properties</span><span style="color: #000000;">))        {            </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$obj</span><span style="color: #000000;">;        }        </span><span style="color: #0000ff;">foreach</span> ((<span style="color: #0000ff;">array</span>)<span style="color: #800080;">$properties</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$name</span> => <span style="color: #800080;">$value</span><span style="color: #000000;">)         {            </span><span style="color: #800080;">$obj</span>-><span style="color: #800080;">$name</span> = <span style="color: #800080;">$value</span><span style="color: #000000;">;        }        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$obj</span><span style="color: #000000;">;    }    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    * 获取依赖    * @param string $class 类、函数 名称    * @return array    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">function</span> getDependencies(<span style="color: #800080;">$class</span><span style="color: #000000;">)    {        </span><span style="color: #0000ff;">if</span>(<span style="color: #0000ff;">isset</span>(<span style="color: #800080;">$this</span>->_reflections[<span style="color: #800080;">$class</span><span style="color: #000000;">]))        {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果已经实例化过 直接从缓存中获取</span>            <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">array</span>(<span style="color: #800080;">$this</span>->_reflections[<span style="color: #800080;">$class</span>], <span style="color: #800080;">$this</span>->_dependencies[<span style="color: #800080;">$class</span><span style="color: #000000;">]);        }        </span><span style="color: #800080;">$dependencies</span> = <span style="color: #0000ff;">array</span><span style="color: #000000;">();        </span><span style="color: #800080;">$ref</span> = <span style="color: #0000ff;">new</span> ReflectionClass(<span style="color: #800080;">$class</span>);<span style="color: #008000;">//</span><span style="color: #008000;">获取对象的实例</span>        <span style="color: #800080;">$constructor</span> = <span style="color: #800080;">$ref</span>->getConstructor();<span style="color: #008000;">//</span><span style="color: #008000;">获取对象的构造方法</span>        <span style="color: #0000ff;">if</span>(<span style="color: #800080;">$constructor</span> !== <span style="color: #0000ff;">null</span><span style="color: #000000;">)        {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果构造方法有参数</span>            <span style="color: #0000ff;">foreach</span>(<span style="color: #800080;">$constructor</span>->getParameters() <span style="color: #0000ff;">as</span> <span style="color: #800080;">$param</span><span style="color: #000000;">)             {</span><span style="color: #008000;">//</span><span style="color: #008000;">获取构造方法的参数</span>                <span style="color: #0000ff;">if</span>(<span style="color: #800080;">$param</span>-><span style="color: #000000;">isDefaultValueAvailable())                {</span><span style="color: #008000;">//</span><span style="color: #008000;">如果是默认 直接取默认值</span>                    <span style="color: #800080;">$dependencies</span>[] = <span style="color: #800080;">$param</span>-><span style="color: #000000;">getDefaultValue();                }                </span><span style="color: #0000ff;">else</span><span style="color: #000000;">                {</span><span style="color: #008000;">//</span><span style="color: #008000;">将构造函数中的参数实例化</span>                    <span style="color: #800080;">$temp</span> = <span style="color: #800080;">$param</span>-><span style="color: #000000;">getClass();                    </span><span style="color: #800080;">$temp</span> = (<span style="color: #800080;">$temp</span> === <span style="color: #0000ff;">null</span> ? <span style="color: #0000ff;">null</span> : <span style="color: #800080;">$temp</span>-><span style="color: #000000;">getName());                    </span><span style="color: #800080;">$temp</span> = Instance::getInstance(<span style="color: #800080;">$temp</span>);<span style="color: #008000;">//</span><span style="color: #008000;">这里使用Instance 类标示需要实例化 并且存储类的名字</span>                    <span style="color: #800080;">$dependencies</span>[] = <span style="color: #800080;">$temp</span><span style="color: #000000;">;                }            }        }        </span><span style="color: #800080;">$this</span>->_reflections[<span style="color: #800080;">$class</span>] = <span style="color: #800080;">$ref</span><span style="color: #000000;">;        </span><span style="color: #800080;">$this</span>->_dependencies[<span style="color: #800080;">$class</span>] = <span style="color: #800080;">$dependencies</span><span style="color: #000000;">;        </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">array</span>(<span style="color: #800080;">$ref</span>, <span style="color: #800080;">$dependencies</span><span style="color: #000000;">);    }    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    * 解析依赖    * @param array $dependencies 依赖数组    * @param array $reflection 实例    * @return array $dependencies    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">function</span> parseDependencies(<span style="color: #800080;">$dependencies</span>, <span style="color: #800080;">$reflection</span> = <span style="color: #0000ff;">null</span><span style="color: #000000;">)    {        </span><span style="color: #0000ff;">foreach</span> ((<span style="color: #0000ff;">array</span>)<span style="color: #800080;">$dependencies</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$index</span> => <span style="color: #800080;">$dependency</span><span style="color: #000000;">)         {            </span><span style="color: #0000ff;">if</span>(<span style="color: #800080;">$dependency</span><span style="color: #000000;"> instanceof Instance)            {                </span><span style="color: #0000ff;">if</span> (<span style="color: #800080;">$dependency</span>->id !== <span style="color: #0000ff;">null</span><span style="color: #000000;">)                 {                    </span><span style="color: #800080;">$dependencies</span>[<span style="color: #800080;">$index</span>] = <span style="color: #800080;">$this</span>->get(<span style="color: #800080;">$dependency</span>-><span style="color: #000000;">id);                }                 </span><span style="color: #0000ff;">elseif</span>(<span style="color: #800080;">$reflection</span> !== <span style="color: #0000ff;">null</span><span style="color: #000000;">)                 {                    </span><span style="color: #800080;">$parameters</span> = <span style="color: #800080;">$reflection</span>->getConstructor()-><span style="color: #000000;">getParameters();                    </span><span style="color: #800080;">$name</span> = <span style="color: #800080;">$parameters</span>[<span style="color: #800080;">$index</span>]-><span style="color: #000000;">getName();                    </span><span style="color: #800080;">$class</span> = <span style="color: #800080;">$reflection</span>-><span style="color: #000000;">getName();                    </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">Exception</span>('实例化类 ' . <span style="color: #800080;">$class</span> . ' 时缺少必要参数:' . <span style="color: #800080;">$name</span><span style="color: #000000;">);                }               }        }        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$dependencies</span><span style="color: #000000;">;    }}</span>
ログイン後にコピー

以下は、「インスタンス」クラスの内容です。これは主に、クラスの名前を記録し、クラスの名前を記録するために使用されます。インスタンスを取得する必要があります

<span style="color: #0000ff;">class</span><span style="color: #000000;"> Instance{    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*     * @var 类唯一标示     </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">public</span> <span style="color: #800080;">$id</span><span style="color: #000000;">;    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*     * 构造函数     * @param string $id 类唯一ID     * @return void     </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(<span style="color: #800080;">$id</span><span style="color: #000000;">)    {        </span><span style="color: #800080;">$this</span>->id = <span style="color: #800080;">$id</span><span style="color: #000000;">;    }    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*     * 获取类的实例     * @param string $id 类唯一ID     * @return Object Instance     </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">function</span> getInstance(<span style="color: #800080;">$id</span><span style="color: #000000;">)    {        </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span> self(<span style="color: #800080;">$id</span><span style="color: #000000;">);    }}</span>
ログイン後にコピー

その後、動的に属性を追加するために、クラスのインスタンスに動的に属性を追加する機能を実装しました。マジック メソッド '__set' を使用する必要があります。実装するには、依存関係の読み込みを使用するすべてのクラスがこのメソッドを実装する必要があります。次に、最初に次の内容を持つ基本クラス 'Base.class.php' を定義します

<span style="color: #0000ff;">class</span><span style="color: #000000;"> Base{    </span><span style="color: #008000;">/*</span><span style="color: #008000;">*    * 魔术方法    * @param string $name    * @param string $value    * @return void    </span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __set(<span style="color: #800080;">$name</span>, <span style="color: #800080;">$value</span><span style="color: #000000;">)    {        </span><span style="color: #800080;">$this</span>->{<span style="color: #800080;">$name</span>} = <span style="color: #800080;">$value</span><span style="color: #000000;">;    }}</span>
ログイン後にコピー

然后我们来实现'A,B,C'类,A类的实例 依赖于 B类的实例,B类的实例依赖于C类的实例

'A.class.php'

<span style="color: #0000ff;">class</span> A <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Base{    </span><span style="color: #0000ff;">private</span> <span style="color: #800080;">$instanceB</span><span style="color: #000000;">;    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(B <span style="color: #800080;">$instanceB</span><span style="color: #000000;">)    {        </span><span style="color: #800080;">$this</span>->instanceB = <span style="color: #800080;">$instanceB</span><span style="color: #000000;">;    }    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> test()    {        </span><span style="color: #800080;">$this</span>->instanceB-><span style="color: #000000;">test();    }}</span>
ログイン後にコピー

'B.class.php'

<span style="color: #0000ff;">class</span> B  <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Base{    </span><span style="color: #0000ff;">private</span> <span style="color: #800080;">$instanceC</span><span style="color: #000000;">;    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(C <span style="color: #800080;">$instanceC</span><span style="color: #000000;">)    {        </span><span style="color: #800080;">$this</span>->instanceC = <span style="color: #800080;">$instanceC</span><span style="color: #000000;">;    }    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> test()    {        </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span>->instanceC-><span style="color: #000000;">test();    }}</span>
ログイン後にコピー

'C.class.php'

<span style="color: #0000ff;">class</span> C  <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Base{    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> test()    {        </span><span style="color: #0000ff;">echo</span> 'this is C!'<span style="color: #000000;">;    }}de</span>
ログイン後にコピー

然后我们在'index.php'中获取'A'的实例,要实现自动加载,需要使用SPL类库的'spl_autoload_register'方法,代码如下

<span style="color: #0000ff;">function</span> autoload(<span style="color: #800080;">$className</span><span style="color: #000000;">){    </span><span style="color: #0000ff;">include_once</span> <span style="color: #800080;">$className</span> . '.class.php'<span style="color: #000000;">;}spl_autoload_register(</span>'autoload', <span style="color: #0000ff;">true</span>, <span style="color: #0000ff;">true</span><span style="color: #000000;">);</span><span style="color: #800080;">$container</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Container;</span><span style="color: #800080;">$a</span> = <span style="color: #800080;">$container</span>->get('A'<span style="color: #000000;">);</span><span style="color: #800080;">$a</span>->test();<span style="color: #008000;">//</span><span style="color: #008000;">输出 'this is C!'</span>
ログイン後にコピー

上面的例子看起来是不是很爽,根本都不需要考虑'B','C' (当然,这里B,C 除了要使用相应类的实例外,没有其他参数,如果有其他参数,必须显要调用'$container->set(xx)'方法进行注册,为其制定实例化必要的参数)。有细心同学可能会思考,比如我在先获取了'A'的实例,我在另外一个地方也要获取'A'的实例,但是这个地方'A'的实例需要其中某个属性不一样,我怎么做到?

你可以看到'Container' 类的 'get' 方法有其他两个参数,'$params' 和 '$properties' , 这个'$properties' 即可实现刚刚的需求,这都依赖'__set'魔术方法,当然这里你不仅可以注册类,也可以注册方法或者对象,只是注册方法时要使用回调函数,例如

<span style="color: #800080;">$container</span>->set('foo', <span style="color: #0000ff;">function</span>(<span style="color: #800080;">$container</span>, <span style="color: #800080;">$params</span>, <span style="color: #800080;">$config</span><span style="color: #000000;">){    </span><span style="color: #008080;">print_r</span>(<span style="color: #800080;">$params</span><span style="color: #000000;">);    </span><span style="color: #008080;">print_r</span>(<span style="color: #800080;">$config</span><span style="color: #000000;">);});</span><span style="color: #800080;">$container</span>->get('foo', <span style="color: #0000ff;">array</span>('name' => 'foo'), <span style="color: #0000ff;">array</span>('key' => 'test'));
ログイン後にコピー

还可以注册一个对象的实例,例如

<span style="color: #0000ff;">class</span><span style="color: #000000;"> Test{    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> mytest()    {        </span><span style="color: #0000ff;">echo</span> 'this is a test'<span style="color: #000000;">;    }}</span><span style="color: #800080;">$container</span>->set('testObj', <span style="color: #0000ff;">new</span><span style="color: #000000;"> Test());</span><span style="color: #800080;">$test</span> = <span style="color: #800080;">$container</span>->get('testObj'<span style="color: #000000;">);</span><span style="color: #800080;">$test</span>->mytest();
ログイン後にコピー

 

 以上自动加载,依赖控制的大体思想就是将类所要引用的实例通过构造函数注入到其内部,在获取类的实例的时候通过PHP内建的反射解析构造函数的参数对所需要的类进行加载,然后进行实例化,并进行缓存以便在下次获取时直接从内存取得

 

以上代码仅仅用于学习和实验,未经严格测试,请不要用于生产环境,以免产生未知bug

 

鄙人才疏学浅,有不足之处,欢迎补足!

4楼玻璃鱼儿
PHP还是适合过程化程序编写,语言特征决定的。
Re: 八面碰壁居士
@玻璃鱼儿,恩,面向过程看起来比较简单,有不少优势。不过当项目大时,就会越来越乱。通常将面向过程和对象结合起来比较好!
3楼freephp
好复杂,特别是后面实现自动加载的。能不能简化一下代码?
Re: 八面碰壁居士
@freephp,恩,好的,我将抽时间对上面每步进行拆解并进行解释
2楼batsing
后面的 spl_autoload_register 不是直接用 __autoload 更简化吗
1楼卓酷
我怎么觉得还是改动一下mail类更方便呢?后面又是接口又是依赖注入的,绕来绕去都懵了。,感觉对于PHP这样的纯粹动态脚本语言来加讲,不要把它当作静态语言来用,否则有很多的特性不但发挥不出来。
Re: 八面碰壁居士
@卓酷,当然这里只是阐述这个思想,如果仅仅改动mail类,在需求不断增加和改动的情况下会使该类十分臃肿,并且产生很多if语句,不利于维护。至于性能问题,我觉得在当前实际情况下瓶颈不在于php,而更多的在于网络和存储方面。性能论不是绝对的,要和开发维护成本进行比较,当前例子极大的降低了维护成本,而带来的性能问题微乎其微甚至在某些情况下还有提升,此处缓冲了类的实例,减少了大部分开销
関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート