はじめに
ソフトウェアとアーキテクチャの再利用については、長らく技術関連の出版物でうるさいくらいに提唱されてきましたが、いまだに再利用の原則に反するコードや設計がよく見受けられます。例えば、図1のような内部構造を持つソフトウェアシステムを何度目にしたか分かりません。あるいは、いざコードを再利用しようとしたら、求める機能を備えたコンポーネント(コンポーネントE)は既にあるものの、その動作が別のコンポーネント(作成中のシステムには必要のない機能を持つコンポーネントC)に依存しているため、再利用が不可能だと分かることもあります。仮にコンポーネントCも併せて再利用することに決めたとしても、今度はそれがまた別のコンポーネントAに依存している場合も考えられます。
このような依存関係は、今でも多くのソフトウェアプロジェクトに当たり前に見られます。もちろん、そういったコードは意図して作られたものではありません。通常、チームはアーキテクチャのレイアウトを決めてからシステムのコーディングに取りかかります。しかしコードの作成を進めるにつれて、悪しき依存関係が知らぬ間に紛れ込んだり、偶発的に生み出されたりします。そして、開発サイクルがずっと先に進むまで、そのような意図しない依存関係にチームが気付かないこともよくあります。
![図1 典型的なソフトウェアシステム。多くのソフトウェアでは、この図のような入り組んだ相互関係がコンポーネント間に成立している 図1 典型的なソフトウェアシステム。多くのソフトウェアでは、この図のような入り組んだ相互関係がコンポーネント間に成立している](http://cz-cdn.shoeisha.jp/static/images/article/1112/18348.gif)
新しいオープンソースのツール「Verifydesign」は、コードを監視して、意図しない依存関係を防ぐのに役立ちます。図2に、整理されたソフトウェアコンポーネント間の関係を示します。図2では、APIの存在を黄色いバーで表しています。コンポーネントの通信は、このAPIを通じてのみ行われます。図1では、コンポーネントの関係を示す線をわざとコンポーネントの中心から中心へと引きました。これが現在のほとんどのソフトウェアシステムの実態だからです。図1で強調したのは、多くのソフトウェアシステムがAPIを通じて通信するのを面倒くさがって、図のようにコンポーネントの中心へ直接通信していることです。それに対して図2は、図1のような循環する依存関係が排除された設計を示しています。
![図2 理想的なソフトウェアシステム。このアプリケーションでは、コンポーネント間に直線的な関係のパスが成立している 図2 理想的なソフトウェアシステム。このアプリケーションでは、コンポーネント間に直線的な関係のパスが成立している](http://cz-cdn.shoeisha.jp/static/images/article/1112/18349.gif)
VerifyDesignでは、ソフトウェアコンポーネント間の依存関係を定義できます。例えば図2には以下の依存関係があります。
- コンポーネントAの実装はコンポーネントBのAPIに依存する
- コンポーネントAの実装はコンポーネントCのAPIに依存する
- コンポーネントAの実装はコンポーネントDのAPIに依存する
この記事では、Verifydesignツールを使って依存関係を定義および強制する方法について、具体例を挙げて解説します。
Verifydesignツールをチームで使用するには
以降では、Verifydesignを利用するシステムの例を紹介します。このシステムは、電話と電話制御クライアントから構成されます。クライアントは電話のAPIに依存します。電話のAPIは何にも依存せず、電話の実装は電話のAPIのみに依存します。以降の説明では、まだこの設計を理解していない新参の開発者がコードを作成するという状況を想定して話を進めます。設計の依存関係ルールに反するコードをわざと作成してみて、Verifydesignが設計の遵守にどのような威力を発揮するのかを実証したいと思います。今回の記事では、この「望ましい設計」のことを、現状で実装されている設計と対比する概念として、「パッケージ設計」と呼ぶことにします。
この記事のダウンロードサンプルには、次の内容の「verifydesign/packagedesign/bldfiles/design.xml」というファイルが含まれています。
<design> <!--The phone's api is defined to depend on nothing --> <package name="phoneApi" package="biz.xsoftware.api.phone"/> <!--The phone's implementation obviously implements and depends on the api --> <package name="phoneImpl" package="biz.xsoftware.impl.phone"> <depends>phoneApi</depends> </package> <!--The client is defined to depend on the phone's api (not the phone's implementation) --> <package name="client" package="biz.xsoftware.impl.client"> <depends>phoneApi</depends> </package> </design>
まず、この段階でビルドを実行し、ダウンロードしたコードが設計に違反しないことを確認してみましょう。ビルドを実行するには、「verifydesign/packagedesign」ディレクトリに移動し、次のコマンドを実行します。
ant --f bldfiles/build.xml
「BUILD SUCCESSFUL」というメッセージが表示されるはずです。前掲のXMLファイルには、ソースコードに許される依存関係の基本ルールが含まれています。このファイルでは3つのパッケージを宣言しています。この「パッケージ設計」を図に示したのが図3です。
![図3 強制された相互関係。この図は、「design.xml」ファイル内で宣言した相互関係がパッケージ間に強制されることを示している 図3 強制された相互関係。この図は、「design.xml」ファイル内で宣言した相互関係がパッケージ間に強制されることを示している](http://cz-cdn.shoeisha.jp/static/images/article/1112/18350.gif)
図3では、ファイルで宣言された各パッケージをボックスで表し、依存関係を矢印で表します。これが、Javaコードに許される依存関係のすべてです。ここで実現しようとしている環境は、例えばプログラマがコードを変更してクライアントがPhoneImplに依存するようになったときに警告のメッセージが表示されるというものです。それでは、Verifydesignの機能について詳しく説明していくことにしましょう。ここでは、この設計について何も知らない開発者が依存関係のルールを無視しようとしたときにVerifyDesignがどのように役立つかを明らかにします。