PHP 5でのOOPワークショップ:PHPでの不純物の2つの異なる実装のエラー、利点、欠点の分析

不純物に関する私の記事のリリース後、 記事へのコメントとプライベートメッセージで議論を続けました。 今日、私は私のブログで、 Leonid Schleicherの実装に対する私の実装の利点として私が見るものを説明するように頼んだ読者からのコメントを見ました。



自分のサイトの投稿にコメントで返信しようとしていましたが、質問が非常に興味深いことが突然判明しました。 むしろ、質問そのものではなく、私が実施した分析であり、答えを準備しています。



レオニードとの二人のうちの一人が、あらゆる規模のプロジェクトに不純物を普遍的に導入する任務を自分自身で設定したとは思えません。 どちらの実装も、私にとってはホームフレームワークのレベルをはるかに超えていないようで、eval()なしで不純物を実装するための概念であり、作業ツールです。 しかし、それでも、比較を詳しく見てみましょう。



私のバージョンは、不純物に関するキャッシュ情報を保存するためにいわゆるレジストリを使用しているため、より生産的です。 しかし、私が使用したキャッシュオプションは、不純物のメソッド/プロパティが繰り返しアクセスされる場合にのみ有効です。 アクセスが1回限りの場合、最初のキャッシュを作成する必要があるため、私のバージョンでは、Leonidの場合よりもかなり遅くなります。 私の場合、この欠点を解消するには、静的レジストリクラスのモデルからシングルトンレジストリモデルに切り替え、抽象ファクトリを介して初期化するか、フレームワークレベルの標準の「レジストリ」パターンを実装する必要があります。 これにより、プロジェクトの要件に応じてレジストリの実装を変更できます。たとえば、キャッシュをメソッドの1回限りの呼び出しを高速化する直接呼び出しに置き換えることができます。



読者が述べたように、キャッシュの実装はコードを複雑にし、エラーを起こしやすくしました(これらは空の単語ではなく、デバッグ中にいくつかのバグをキャッチし、テストを書くのが面倒でした)、そしてもちろん理解するのがいくらか難しくなりました。



私とLeonidの両方が、ガベージコレクションに関連する同じ実装エラーを作成したようです。 循環参照に注意してください:不純物のクラスのインスタンスへの参照はアグリゲータークラスに格納され、不純物の順に、アグリゲーターのクラスのインスタンスへの参照が格納されます。 PHPバージョン5.3より前では、ガベージコレクターは循環参照を処理する方法を知らないため、メモリリークが発生します。 5.3では、この目的のために特別なガベージコレクターをアクティブにできますが、スクリプト全体が遅くなるため、これは望ましくありません。 何がそんなに悪いの? それはすべて、不純物が使用される理由と方法に依存します。 不純物を含むアグリゲーターオブジェクトが多数ある場合、それらは大きく複雑であり、多くの場合作成/破壊されます-すべてが非常に悪いでしょう。 不純物がスクリプトの存続期間中に存在するアグリゲーターと混合されている場合、スクリプトが占有するすべてのメモリが完了時に解放されるため、何も問題はありません。



このエラーを修正するには? どちらの実装でも、あまり使用されないPHPの機会であるデストラクタを利用して、これを行うのは非常に簡単です。 所有クラスへの参照を強制的に無効化するデストラクタを混合物に追加し、アグリゲータクラスにデストラクタを追加して、このクラスの不純物のリストをクリアする必要があります。



Callerクラスの魔法の__call()メソッドは保護できないため、Leonidのバージョンはまったくコンパイルされないことが判明しました。 修正、獲得。 コメントのバージョン5.3 では、このエラーは存在しなくなりました。



レオニードは、不純物がアグリゲータークラスのメンバーを独自のものとして参照できるように、不純物にマジックメソッドを実装しました。 これを行うとは思いませんでした。 ただし、このアイデアを自宅で実装するかどうかを検討します。 これまでのところ、同時に取得されるコールグラフと、スコープの曖昧さはあまり好きではありません。



Leonidバリアントは静的です。 何らかの理由で、アグリゲーターのコンストラクターで不純物を作成する必要があるようにコードが記述されています。これは一般に、不純物の概念とわずかに矛盾しています。 私のバージョンでは、不純物はクラスに動的に接続されており、クラス自体をロードする必要さえありません(アグリゲーターも不純物もありません)。 つまり、パフォーマンスを犠牲にすることなく、プロジェクトの1つの場所ですべての不純物を結び付けることができます。 これには欠点もあります。アグリゲータークラスの名前の誤りや不純なものを追跡するのはより困難です。 結局、クラスは実際に使用されるまでロードされません。



レオニードの亜種の不純物は、継承によってのみ実現されます。 私のバージョンでは、構成とともに継承も使用できます。 これは、クラスがアグリゲータークラスから継承できない場合に便利です。



私のバージョンの「接続」により、メインコードの実行中に不純物をクラスに直接接続できます。 つまり、注意を怠ると、この混合物がクラスの1つのインスタンスに混合され、別のインスタンスからではないことが判明する場合があります。 過度の柔軟性が得られました。必要に応じて簡単に削除できます。 ところで、これはキャッシュ(レジストリ)を調べることで実行できます。



Leonidのバリアントは、アグリゲータークラスのすべてのマジックメソッドを実装しているわけではありません(主な重点はメソッドにあります)が、これは簡単に修正できます。



どちらのオプションも、同じ名前の不純物メソッドが存在する状況に等しく反応します。最初の不純物メソッドが呼び出されます。 私の意見では、これも適切な選択肢ではありません。 ただし、特に不純物を迅速に実行する必要がある場合は、検証の実装に多少のコストがかかります。このアグリゲーターのすべてのクラスの不純物をロードせずに重複の存在を検証することはできません。 だから、私たち二人は我慢することにしたようです。



Leonidは、存在しない名前でアグリゲーターメソッドを呼び出そうと試みた場合、その状況でエラーのチェックを追加するのを忘れていました。 結果の実装ではエラー状態がなく、何も起こらないため、これは悪いことです。 それともそうだったのでしょうか? 一般的に、私の観点からすると、これは悪いことです。 エラーを取得します。 ただし、すべてが簡単に修正されます。



Leonidには、不純な名前の保護されたメソッドまたはプライベートメソッドを定義し、アグリゲーターでその名前のメソッドを呼び出そうとすると、別の奇妙な状況があります。 呼び出しグラフに注意してください。 __call()はアグリゲーターで呼び出され、method_exists()を使用して不純メソッドの中から指定された名前でメソッドが検索されます。 隠されたメンバーおよび保護されたメンバーのmethod_exists()もtrueを返すため、一致が検出されます。 不純物インスタンスで既にこのメソッドを呼び出そうとしました。 それでもメソッドはこのスコープから保護され、アクセスできないため、__ call()マジック不純メソッドが既に呼び出されています。このメソッドは、Leonidがエラーチェックを追加するのを忘れた__access_call疑似マジックアグリゲーターメソッドを呼び出します。 エラーチェックがないため、何も起こらず、エラーも発生しませんが、個人的には希望します。 はい、そして私の意見では、グラフ自体は80行に不純物を実装するためにかなり複雑でした...もちろん、間違いを修正するのは簡単です。



記事の解説で、著者は「各親クラスはそのすべての不純物のインスタンスを作成する必要がある」と述べています。 しかし、彼はすべてに共通のインスタンスを使用して、レジストリを使用してそれらを取得できます。 この場合、不純物に独自の状態がない場合にのみ、レジストリを介して既製のコピーを取得すると便利です。 そうしないと、アイデアの意味が失われ、別の方法を探す必要があります。



サイトのコメントで、著者は、静的不純物メソッドのサポートを追加したことに言及しています。 私のバージョンでは、これには静的メソッドのレジストリに追加のキャッシュを作成する必要があります。 もう少し体の動き。 今のところ、私は控えるでしょう、私は推測します。



Leonidの実装には、アグリゲーターの擬似魔法のメソッドが含まれており、パブリックインターフェイスでクラスの非表示および保護されたメンバーへのアクセスを実際に開くことに注意してください。 良くも悪くも、プロジェクト次第です。 原則として、通常のプログラマーは、2つのアンダースコアで始まるメソッドが体系的で内部的なことを行い、フレームワークの内部によってのみ呼び出されることを既に理解している必要があります。 したがって、これは特定の問題を引き起こしません。



追加のチェックには、PHPのタイピングヒントを積極的に使用しています。 しし座流星は気づきませんでしたが、これは何もありません。



PHP 5.3では、静的呼び出しの不純物のリストを含む静的プロパティがコメントページに追加されました 。 一見不純物が静的とインスタンスに分けられているように見えるので、文脈からは少し外れていると思いますが、これは真実ではありません。



ところで、著者のバージョンは既にプロジェクトで機能していますが、私のバージョンはまだ機能していません。 アイドル状態のコンピューターに横たわって...



無関係なことから、サイトのコードを読んだときに、 構文強調表示されていないかどうかを確認したいので、少なくともインデントが正常になるようにします。 Leonidの分析用コードは、Zend Studioでフォーマットする必要がありました。 サイトのコードが部外者による分析を目的としていない場合は謝罪します。 言い訳になるのは、それでもオープンソースから入手できるということだけです。



レオニードのサイトのコメントでは、通常の委任に対する不純物の利点についても質問がありましたが、これは次の記事のトピックであり、それほど長くはかかりません。 このボリュームでは、この問題に関する私の見解をここで述べることはできません。



「悪い/良い」という点で2つの実装を比較しないようにというコメントの大きな要求。 自分を尊重します。 長所と短所について言えば、民主主義は控えましょう。 事実、事実、そして事実。 問題を理解することなく「UG」と「KG / AM」と叫ぶのが大好きなトロールは、着陸するように求められ 、列車はすぐに出発します。



ご清聴ありがとうございました。



All Articles