Forwardingデコレータでモジュラーアプリケーションアーキテクチャを構築する(著者翻訳)

開発者が自分の将来のWebアプリケーションのアーキテクチャを計画するとき、事前にその拡張性について考えることが有用です。 アプリケーションのモジュール式アーキテクチャは、高度な拡張性を提供できます。 そのようなアーキテクチャを実装する方法はかなりありますが、それらの基本原則はすべて類似しています:概念の分離、自給自足、すべてのコンポーネントの相互互換性。



ただし、PHPではめったに見られないアプローチが1つあります。 これには、ネイティブ継承の使用が含まれており、コードのパッチを「改善」することができます(c)。 このメソッドを「Forwarding Decorator」と呼びます。 私たちには非常に効果的であるように思われますが、生産的にはそれほど重要ではありませんが、ところで、壮観でもあります。



SitePointで公開された元の英語の記事「 Forwarding Decoratorsを使用したモジュラーアーキテクチャの作成 」の著者として、著者の翻訳版を紹介します。 その中で、私はもともと考えられていた意味とアイデアを保持しましたが、フローを最大化しようとしました。



はじめに



この記事では、Forwarding Decoratorを使用したアプローチの実装と、その長所と短所について検討します。 このアプローチを、フック、コードパッチ、またはDI(依存性注入)を使用する他のよく知られている選択肢と比較してください。 わかりやすくするために、このGitHubリポジトリにデモアプリケーションがあります



主なアイデアは、各クラスをサービスと見なし、コードをコンパイルするときに継承者のチェーンを継承および反転することでこのサービスを変更することです。



このような考えに基づいたシステムでは、どのモジュールでも特別なデコレータークラス(特別な方法でマークされた)を作成できます。 このようなクラスは、継承メカニズムを介して別のクラスのフィールドとメソッドを受け取りますが、コンパイル後は元のクラスの代わりにどこでも使用されます。

画像




実際、そのようなクラスをフォワーディングデコレーターと呼んでいるのはこのためです。これらのデコレーターは元の実装の上位構造ですが、使用する場所で前方に移動します。



このアプローチの利点は明らかです。





ただし、このアプローチには欠点もあります。





システムを拡張する同様の方法は、ある意味で、直接的なコードパッチ(低レベル、ゲームルールなし、ゴッドモード、最大のパワー、ただし最大の責任など)とプラグインアーキテクチャの中間的なソリューションです。サブシステムとその変更方法が提供するプラグインがあるかもしれません。 デコレータシステムを使用すると、特定の範囲の問題を適切に解決できますが、これは特効薬ではなく、モジュール性を整理する理想的な方法ではありません。



このようなシステムを使用するにはどうすればよいですか?



以下に例を示します。



class Foo { public function bar() { echo 'baz'; } }
      
      





 namespace Module1; /** *    ,    DecoratorInterface ( :    ,   ) */ class ModifiedFoo extends \Foo implements \DecoratorInterface { public function bar() { parent::bar(); echo ' modified'; } }
      
      





 // ... -    $object = new Foo(); $object->bar(); // will echo 'baz modified'
      
      





どうして起こったの? これはストリートマジックです)継承のチェーンを元に戻しています。 ソースクラスには内部コードがありません。 コンパイルの結果として、ソースクラスのコンテンツがチェーンの新しい親になる別のクラスに入るように、コードを前処理します。



 //    ,   ,     class Foo extends \Module1\ModifiedFoo { // move the implementation from here to FooOriginal }
      
      





 namespace Module1; //     ,         abstract class ModifiedFoo extends \FooOriginal implements \DecoratorInterface { public function bar() { parent::bar(); echo ' modified'; } }
      
      





 //      .        class FooOriginal { public function bar() { echo 'baz'; } }
      
      





要するに、コンパイラは、中間クラスを構築するアプリケーションと、元のクラスの代わりにこれらの中間クラスをロードするオートローダーに組み込まれています。



そしてもう少し。 コンパイラーは、システムで使用されるすべてのクラスのリストを作成し、デコレーターではない各クラスについて、 DecoratorInterfaceを使用してそれを装飾するすべてのサブクラスを見つけます。 彼はデコレーターのツリーを作成し、ループがあるかどうかを確認し、優先順位に従ってデコレーターをソートします(詳細については後で説明します)。継承チェーンが反対方向にデプロイされる中間クラスを構築します。 ソースコードは新しいクラスに変換され、継承チェーンの新しい親クラスになります。



複雑に聞こえます。 つまり、本当に複雑で複雑なシステムです。 ただし、モジュールを非常に柔軟に組み合わせることができ、これらのモジュールを使用すると、アプリケーションの任意の部分を完全に変更できます。



そして、1つのクラスが複数のモジュールによって上書きされた場合はどうなりますか?



複数のデコレータが同時にゲームに参加すると、優先度に従ってデコレーションチェーンに分類されます。 優先度はアノテーション(Doctrine \ Annotationsを使用)または設定を使用して設定できます。



例を考えてみましょう:



 class Foo { public function bar() { echo 'baz'; } }
      
      





 namespace Module1; class Foo extends \Foo implements \DecoratorInterface { public function bar() { parent::bar(); echo ' modified'; } }
      
      





 namespace Module2; /** * @Decorator\After("Module1") */ class Foo extends \Foo implements \DecoratorInterface { public function bar() { parent::bar(); echo ' twice'; } }
      
      





 // ... -    $object = new Foo(); $object->bar(); //  'baz modified twice'
      
      





この例では、 デコレータ\ Afterアノテーションを使用して、モジュール2の前に別のモジュール1のデコレータを配置します。コンパイラはファイルを分析し、アノテーションを考慮して、次の継承チェーンを持つ中間クラスを構築します。

画像

次の注釈も使用できます。





この一連の注釈(Before、After、Depend)は、モジュールとクラスの任意の組み合わせを構築するのに絶対に十分です。



実用的な例はありますか?



あります! 明確にするために、このGitHubリポジトリにあるアプリケーションのデモを用意しました。 このPHPで作成されたアプリケーションにはモジュール式のアーキテクチャがあり、モジュールは再コンパイルせずにコードを混在させることができます。 同時に、モジュールを追加および削除できますが、この場合、再コンパイルがすでに必要です。 これらはすべて、 readmeファイルで詳細に説明されています。



かなり「戦闘」の例があります。 このアプローチを使用するソフトウェア製品がすでにいくつか市場に出回っています。 特に、OXID eShopでは非常によく似たものが使用されます。 ところで、彼らはクールなブログ投稿スタイルを持っています。 X-Cart 5の別のプラットフォームでは、このアプローチは、私が説明した形式で正確に実装されています-X-Cart 5コードは、この記事の基礎としても採用されました。 これにより、開発者の想像力(または顧客のお金=)だけ拡張でき、その後のカーネルアップグレードを中断することなく、eコマース用の非常に柔軟なソリューションを作成できました。



使い慣れたフックとパッチの方が優れています! かどうか?



Forwarding Decoratorsのアプローチと同様に、フックと前面パッチの使用には長所と短所があります。





おわりに



転送デコレータは、少なくとも注目に値するアプローチです。 PHPで拡張可能なモジュラーアプリケーションアーキテクチャを開発する問題を解決するために使用できます。 これは、継承やフィールド/メソッド/クラスのスコープなどのよく知られた構造を使用します。



このような概念を実装するのは簡単な作業ではなく、デバッグには困難があるかもしれませんが、コンパイラーを適切に構成するのにある程度の時間を費やせば克服できます。



この資料に興味がある場合は、次の記事で、オートローダーで最適なコンパイラーを作成し、ストリームフィルター(PHPストリームフィルター)を使用して、XDebugによるソースコードの段階的なデバッグを有効にする方法を説明します。 面白い? コメントで教えてください。 そして、私はあなたの質問、アドバイス、建設的な批判に喜んでいます。



All Articles