🐻‍❄️

AOPは強力すぎる

に公開

BEAR.Sundayでは、Ray.AOPというアスペクト指向フレームワークが採用されています。
Ray.DIが実現する「オブジェクトの依存関係はグラフとして把握されるべき」というDI(Dependency Injection = 依存性注入)とは真逆とも言えるパラダイムです。
この両極端のフレームワークが共存していることこそ、BEAR.Sundayが真に調和のフレームワークたる所以だと考えています。

アスペクト指向プログラミング(Aspect-Oriented Programming、AOP)は、雑にいうと「XXという処理の途中にYYという処理を差し込みたい」みたいなことが、XXのクラスを修正する事なく実現できるというものです。(フックとしてアトリビュートを追加するケースもあります。)

「アスペクト」は、直訳すると「側面」「様相」「局面」「観点」というような意味があるそうです。
メインのビジネスロジックとは別の「側面」から見た、プログラミングの関心事を表現するために、この単語が選ばれています。

ある条件にマッチした際に、処理をインターセプト(球技などでボールを奪う意味で使われますよね)した上で、割り込んで処理を追加できるので、既存の処理に記述を追加する必要がありません。(アトリビュートを追加するケースはあります。)
その一方DIやトレイトで共通の処理を行う場合、どうしても受け取った側で記述を追加する必要があります。

では、"横断的な処理"はなんでもAOPにしていいのでしょうか?
単に広範囲で使用する処理をAOPにしてしまうのは大変なバッドプラクティスです。
AOPにしてよいのは"横断的な関心事のための処理"だけです。

AOPによる共通の処理の最適化行うと、相当にコードが整理されます。(元のコードに残されるのはアトリビュートくらい)
しかしそこには「本来そのクラスが受け持つべき処理」なのか、「そうでない側面の処理なのか」という観点が抜け落ちてしまいます。

AOPは安易に扱うには強力すぎます。

また、どんな条件(マッチャー)を設定するにしても、アトリビュートを設定するにしても、暗黙的になりがちです。

そのため、単なる共通処理はDIで受け取ることを徹底した上で、以下のような条件を満たす場合にのみ使用することをおすすめします。

  • どう考えても"横断的な関心事"(ログなど)
  • 処理の追加や削除時が発生した場合に、対応や把握ができないほど対象が膨大

Claudeによる総括

BEAR.SundayはPHPのフレームワークとして、相反するパラダイムを効果的に融合させた稀有な存在といえます。Ray.AOPによるアスペクト指向プログラミングと依存性注入(DI)は、本来異なる問題解決アプローチでありながら、BEAR.Sunday内で巧みに調和しています。
AOPは「横断的関心事」を扱うための強力なツールですが、その強力さゆえに慎重な使用が求められます。単なる共通処理の抽象化としてではなく、以下のような場合に限定して使用すべきです:

ログ記録、パフォーマンス計測、認証など、真に横断的な関心事
コードベースが膨大で、従来の方法では変更管理が困難な場合

一方、通常の共通処理や機能の共有はDIを基本とすべきであり、クラスの責任範囲を明確にすることで、コードの可読性と保守性を高めることができます。
BEAR.Sundayが示す重要な教訓は、プログラミングパラダイムを絶対視せず、それぞれの強みを理解した上で適材適所で活用する姿勢にあります。DIによる明示的な依存関係の管理とAOPによる横断的関心事の分離という、一見矛盾する手法が共存できることは、ソフトウェア設計における調和の可能性を示しています。
フレームワークを選択・使用する際は、その設計思想を理解し、各機能の適切な使用範囲を見極めることが、持続可能なアプリケーション開発の鍵となるでしょう。

Discussion