OOP によって設定されたオブジェクトによるドメイン概念のモデル化という目的は、長い間、十分とは言えない方法で処理されてきました。これまでに試してきた方法の根本的な問題とは何でしょうか。もっと良い方法はないのでしょうか。この記事では、コンポジット指向プログラミング (COP: Composite Oriented Programming) の概念を紹介し、これによって OOP での問題を回避し、再利用可能なコードによってドメイン モデルを構成できるという希望を新たにする方法について説明します。
問題
私が誰かという問題を取り上げましょう。私はさまざまなものです。あるときはソフトウェア開発者であり、新しいソフトウェアを作成しています。あるときはソフトウェア開発者ですが、Java 関連トピックに関するプレゼンテーションを行います。一方、あるときはまったく異なり、銀行の顧客であったり、大学の同窓生であったりします。私はさまざまなものであり、それぞれのコンテキストで 1 つの立場があります。これらの各コンテキストでは、コンテキスト特有の操作によってやりとりする、異なるインターフェイスを使用する必要があります。しかし、すべて異なるインターフェイスを持つ、すべてのコンテキストにおいて、私は 1 つの同じオブジェクトです。銀行にいる私とソフトウェアを作成する私は別の人間ではないのです。
一般的な解決策
それでも、私がソフトウェアでモデル化されるとしたら、おそらく次のようになります。オブジェクト指向プログラミングを使用して、私は Developer クラスとしてモデル化されるでしょう。しかし、このクラスには会合の概念がないので、同窓会に出席する方法がありません。このため、実際上私を複数のクラスでモデル化する必要があります。またはこれらのすべてを 1 つのクラスで実装する必要がある場合は、複数のインスタンス化されたオブジェクトを用意し、代わりに mixin の表記を使用して実装を処理します。
コンポジット
mixin は普通の Java クラスとして実装され、通常はその一部であるコンポジットによって表される特定のインターフェイスを実装します。コンポジットを宣言するには、使用する mixin を注釈によって宣言する Java インターフェイスを作成し、公開するドメイン インターフェイスを記述する "extends" キーワードを使用します。この方法により、コンポジットの構造と動作を確定的に一箇所から定義できます。
COP では、問題を別の実装クラスに分断しておくことは良い考えですが、これらを統合する (構成する) 方法に関する記述は Java インターフェイスにまとめることをお勧めします。このような記述の重複を避けるため、"extends" キーワードを使用して、継承されるインターフェイスの宣言を再利用します。継承されるインターフェイスをこのようにして変更すると、それを継承したコンポジット インターフェイス宣言が自動的に変更されるので、個々に変更する必要がありません。
この方法では、両者の長所を活かすことができます。つまり、それぞれが特定のタスクを処理する別の実装クラスへの問題の分離と、最終的なコンポジットがどのようなものかについての宣言の集中化と明確化です。コンポジットの開発者は何がコンポジットで定義され、何が定義されないかを管理します。
コード サンプル
実際にこれがどのようになるか、私を例として考えてみましょう。私を 1 つのコンポジットとすると、次のように記述できます。
@Mixins({DeveloperMixin.class, SpeakerMixin.class, AlumniMixin.class}) public interface HumanComposite extends Developer, Speaker, Alumni, Composite {}
継承されるインターフェイスには呼び出される実際のメソッドが含まれ、Qi4j ランタイムに応じて、クライアントから特定の mixin インスタンスへの呼び出しをルーティングできるコンポジット インスタンスを構成します。ただし、標準 OOP を使用した実装時に通常のドメイン オブジェクトよりインターフェイスが多くなる場合でも、クライアントの観点からは、これは単なる通常の Java オブジェクトであることに注意してください。また、Developer などのドメイン インターフェイスは Qi4j 特有なものではない標準のインターフェイスであり、実装そのものも、そのインターフェイスを実装する単なる Java クラスにすぎません。しかし、オブジェクトのアイデンティティは個々の mixin インスタンスではなくコンポジット インスタンスによって定義されます。これにより、アイデンティティの危機を解決し、"私" の参照をオブジェクトとしてシステムに送ったり、特定のコンテキストで役立つインターフェイスにキャストできます。ドメインやコンテキストがさらに導入された場合、コンポジットを継承してそれらを処理することもできます。
Alumni インターフェイスと実装を使用する別のコンポジットを作成するには、そのインターフェイスにも Alumni を継承させ、同じ mixin を使用するように宣言します。これにより、基本クラスの複数の継承と再利用に関する通常の問題は回避されます。
構造の問題
多くの場合、ソフトウェアは紙の上でモジュールとレイヤを使用して設計されます。次のような図は非常に一般的です。
この図には、互いに積み重ねられたレイヤの一部であるモジュールが含まれます。これを Layered Modules Metaphor (LMM) と呼ぶことにしましょう。LMM は、詳細にあまり入り込まずにアプリケーション全体の概要を伝えるために使用します。プロジェクトの LMM に厳密に従うことにより、システム規模のバグを減らして、長期的な保守コストを削減することができ、変化に対応できる柔軟性の高いシステムになります。大多数のプロジェクトは LMM を使用してアプリケーションの構造を説明し、多くのプロジェクトが LMM に従おうとしますが、それを実現できるプロジェクトはほとんどありません。たとえば、インフラストラクチャ レイヤのクラスが Web レイヤのクラスを使用するという恐ろしい例を誰でも見たことがあるはずです。
構造の提供
Qi4j は開発者をチームにまとめるため、大胆な方法で LMM を明確にサポートしています。Qi4j のアプリケーション構造は一連の簡単なルールになっています。
- 構造
- すべての構造は起動時に静的に宣言される。
- すべてのコンポジット インスタンスはモジュールに属する。
- すべてのサービスはモジュールに属する。
- すべてのモジュールはレイヤに属する。
- すべてのレイヤは 0 個以上の他のレイヤ上にある (ただし循環しない)。
- すべてのレイヤはアプリケーションを構成する。
- アクセス
- モジュールは同じレイヤ内にある他のすべてのモジュールをアクセスできる。
- レイヤはすぐ下のレイヤをアクセスできる (他動的ではない)。
- 可視性
- デフォルトではコンポジット インスタンスはモジュール内でのみ可視である。
- コンポジット インスタンスはレイヤ内およびレイヤ間で可視にすることができる。
これらのルールは実際よりかなり複雑そうに聞こえます。基本的には、モジュールの外またはレイヤの外で明示的にパブリックにしない限り、コンポジット インスタンスは作成されたモジュール内でプライベートです。これは、通常の Java プログラムのクラスやメソッドに対して "public" または "private" 修飾子を設定する方法と同じです。
この構造はオプションではありませんが、単一レイヤの単一モジュールなどの一般的なアプリケーション構造を確立するための便利なメソッドがあります。
構造の使用
ドメイン コードではアプリケーション構造について知っている必要はありません。構造は単に存在するだけですが、@Structure 形式の注釈で記述します。以下はこの例です。
CompositeBuilderFactory は作成時に mixin に注入されます。コードは構造内のその時点から可視のコンポジットをインスタンス化することだけが許されます。
構造を使用するもう 1 つの一般的な状況は、サービスの検索です。要求されたタイプのサービスが同一モジュール内で 1 つだけ検出された場合、追加のアセンブリは必要ありません。サービスの使用は非常に簡単になります。
たとえば、GenericInventory サービスが Bread モジュールで宣言されている場合、各インベントリ サービス インスタンスはその近似によってそれぞれのクライアントにバインドされます。
構造の確立
Qi4j アプリケーションは、アプリケーション コードによって起動される必要があります。最も簡単な起動方法は次のようになります。
もう 1 つの方法は SingletonAssembler を使用する方法です。
SingletonAssembler は、単一レイヤの単一モジュールとして Qi4j アプリケーションを作成する便利なクラスです。
Assembly[][][] を newApplication() メソッドに渡して、"パンケーキ" レイヤ (つまり、最初と最後のレイヤ以外は各レイヤが別のレイヤにはさまれたレイヤ) によるアプリケーション コンテキストを作成することもできます。たとえば、次のようになります。
これは次の構造になります。
最後に任意の複雑なアプリケーション構造では、ApplicationAssembly インスタンスを newApplication() メソッドに渡す必要があります。ApplicationAssembly は、LayerAssemblies を反復的に構築し、それから反復的に ModuleAssembly を構築することで使用します。たとえば、次のようになります。
これにより、次の構造が生成されます。
構造の利点
アプリケーションの明示的なコーディングによる 2 つの主な利点は次のとおりです。
- 近似による解決
- アーキテクチャの実現
つまり、手近なコンポジットに容易にアクセスできること、また手近なコンポジットは高い優先度を持ち、モジュールまたはレイヤのプライベートなコンポジットは外部からアクセスできないということです。サービスはコンポジットなので、サービスの解決はより暗黙的になり、必要なアセンブリ構成は大幅に減ります。
Qi4j の構造の概念によるもう 1 つの興味深い副作用は、すべてのアプリケーションが静的構造を持つので、別々に保守するのではなく、ツールによる抽出と視覚化が可能になることです。これにより、アーキテクト、設計者、およびチーム リーダーは、開発者がアーキテクチャに従っているか、アーキテクチャを回避しようとしているかを追跡できるようになります。
まとめ
この記事では、Java プラットフォーム上で Qi4j によって実装されるコンポジット指向プログラミングの可能性を探りました。従来の OOP の意味でのオブジェクトを実装するコンポジットにより、問題をより適切に分離し、それにより品質の高い再利用可能なコードを作成する方法を見てきました。また、通常は紙の上で定義されるだけで、コードでは定義されないアプリケーションの構造を明確にモデル化するという考えを検討しました。これを実行すると、サービス間の依存関係に関するアーキテクチャ実現および解決をさらに容易に処理できるようになります。また、大規模なシステムを作成する場合も、導入されるコンポーネントやサービスが増えるにつれてシステム自体の重さで崩壊しないようにするために役立ちます。
最後に、COP および Qi4j の概念のほとんどは新しいものではないことを強調しておく必要があります。ここで実行したことは、従来のプログラミング習慣とフレームワークから適切な概念とパターンを特定し、ソフトウェアの作成時にもそのソフトウェアを理解可能かつ保守可能な状態に維持する上でも役立つものを抜き出すことでした。昔からあるメタファーを新しいコンテキストに適応させることは、ソフトウェアに対しても人生全般においても重要です。
著者について
Rickardは、JBoss、XDoclet、 WebWork など、J2EE 開発に関わる複数の OpenSource プロジェクトで活動しています。また、AOP を基盤として使用した SiteVision CMS/ポータル プラットフォームの主要アーキテクトでした。現在は Jayway に勤務し、インターネット中心アプリケーションのニューウェーブに対応したドメイン指向ソフトウェアを開発する方法に関心を持っています。
JayView について
JayView は Jayway 発行の雑誌です。Jayway は Java 開発を中心とする企業です。この雑誌には Jayway の Java に対する情熱が注がれています。
この雑誌には会社の宣伝は書かれていませんし、美辞麗句もありません。
コーディング、優れたソフトウェアの作成への情熱など、基本的にすべて私たちが楽しんでいることだけが書かれています。詳しくは、JayView の Web サイト www.jayway.com/jayviewをご覧ください。
原文はこちらです:http://www.infoq.com/articles/Composite-Programming-Qi4j
(このArticleは2008年11月19日に原文が掲載されました)