望ましくない依存関係を見つける
最初に、最も基本的なルール違反として、定義済みAPIを迂回しようとしたときにどうなるかをお見せします。ファイル「verifydesign/packagedesign/input/javasrc/biz/xsoftware/impl/client/Client.java」を開き、createPhoneTheWrongWay()というメソッドに以下のコードを追加してください。
PhoneImpl phone = new PhoneImpl();
これによってソースコードにどのような変更が加えられたかを図4に示します。赤い線は、付け加えられた依存関係を表します。これは現在実装されている設計であり、設計ファイルに定義されている望ましい「パッケージ設計」ではないことに注意してください。

この変更の後で、ビルドを再度実行します。ビルドには、同じコマンドを使います。
ant --f bldfiles/build.xml
前回のビルドで表示されたビルド成功のメッセージに代わって、次のエラーが表示されます。
You are violating your own design.... Class = biz.xsoftware.impl.client.Client depends on Class = biz.xsoftware.impl.phone.PhoneImpl The dependency to allow this is not defined in your design Package=biz.xsoftware.impl.client is not defined to depend on Package=biz.xsoftware.impl.phone Change the code or the design
最後の1行「Change the code or the design」(コードまたは設計を変更してください)に注目してください。このような表現になっているのは、どんなソフトウェアプロジェクトでもパッケージ設計には常に改良が加えられているという現実を反映してのことです。これはVerifydesignを使うかどうかに関係なく、常に言えることです。Verifydesignはアジャイルプロセスまたはウォーターフォールプロセスに従う多くのプロジェクトで使われていますが、どんなプロセスを使用したとしても、設計は常に変わるものだということが明らかになっています。Verifydesignは、設計の変遷が誰にも気付かれないまま通り過ぎるのを見送る代わりに、大声で指摘します。
次に進む前に、先ほどコードに追加した変更を削除し、ファイルを元の状態に戻してください。ビルドを再度実行すれば、プロジェクトは正常にビルドされます。
外部への依存関係
ときには、外部への依存関係を局地化して、ソースコード内で外部エンティティに依存するパッケージを1つだけに限定することも必要です。これが便利なのは、外部のテクノロジを将来切り替える可能性がある場合です。外部に依存するパッケージを1つだけに限定すれば、切り替え時に書き直すのは1つのパッケージだけで済みます。デフォルト設定のVerifydesignでは、javax内のすべての要素に対して依存関係を宣言できます。また、デフォルトではjava.*に対する依存関係を宣言しなくてかまいません。このデフォルト設定は変更できます。
Phoneクラスがjava.rmiおよびその全サブパッケージ(java.rmi.registryなど)に依存すると仮定します。また、後でコードの変更が必要になることがわかっていて、他のパッケージ(クライアントなど)がjavax.rmiに直接依存することは避けたいと仮定します。例えば、rmiに依存する電話実装に加えて、Webサービスに依存する別の電話実装を後で使う場合などが、このケースに該当します。あるいは、最終的に2種類のPhone実装が必要になることが現時点でわかっている場合もあるでしょう。
このようなケースで最初に必要な作業は、java.*のデフォルト動作をjava.rmiについてのみオーバーライドすることです。「design.xml」ファイルに次の変更を加えてください。
<package name="rmi" package="java.rmi" subpackages="include"/> <package name="phoneImpl" package="biz.xsoftware.impl.phone"> <depends>phoneApi</depends> <depends>rmi</depends> </package>
この変更で、java.rmiのすべてのサブパッケージをrmiパッケージに含めるように設計ツールに指示するsubpackages属性が追加されました。つまり、今後はPhoneImplパッケージが以下のパッケージに依存することが許されます。
- java.rmi
- java.rmi.activation
- java.rmi.dgc
- java.rmi.registry
- java.rmi.server
先のステップに進みましょう。「PhoneImpl.java」に次のコードを追加してください。
public void makeCall(String number) { try { Registry registry = LocateRegistry.createRegistry(5000); } catch(RemoteException e) { throw new RuntimeException(e); } }
この段階での「パッケージ設計」を図5に示します。

これは最適なカプセル化です。クライアントとAPIがRMIExceptionについて関知することはありません。注意が必要なのは、makeCallメソッドでRemoteExceptionをスローできないことです。この例外をスローするにはAPIパッケージ内の「Phone.java」のmakeCallメソッドにthrows RemoteException句を追加する必要がありますが、これはphoneApiからjava.rmi.RemoteExceptionへの依存関係を作ることになるため設計に違反します。phoneApiパッケージがjava.rmi.RemoteExceptionに依存することは許されないので、このような依存関係はビルドのエラーにつながります。思い出してください。java.rmiへの依存が許されるのは電話の実装だけです。APIがrmiに依存することは許されません。これは、クライアントが常に汎用APIを使用し、特定のテクノロジに結び付かないようにするためです。後は自分でいろいろ試してみてください。
新しいパッケージを追加する
次に、biz.xsoftware.client2という新しいパッケージを追加し、このパッケージにClient2.javaクラスを追加してみましょう。このクラスにコードを書く必要はまったくありません。これにより、実際の設計は図6のようになります。これもまた、設計ファイルに定義されている「パッケージ設計」とは違います。

ant?f bldfiles/build.xmlを使ってビルドを再実行してください。以下のエラーが表示されます。
Package=biz.xsoftware.client2 is not defined in the design. All packages with classes must be declared in the design file. Class found in the offending package=biz.xsoftware.client2.Client2
要するに、「パッケージ設計」はモジュール内のすべてのコードをカバーしなければならないということです。Verifydesignで検証を行う場合、設計は常に最新の状態を反映していなければなりません。先ほどの例のようにsubpackagesを使えば、いつでも一連の名前付きサブパッケージをモジュールに含めることができます。実際、このような場合のために予約されているdefault packageを使って、すべてのソースコードを1つの大きな集団として宣言することもできます。
<package name="default" package="default package"
subpackages="include" needdepends="false"/>
しかし、設計を修正する方が適切です。次のコードを設計ファイルに追加してください。
<package name="client2" package="biz.xsoftware.client2"/>
この新しいパッケージを定義しておけば、ビルドを正常に再実行できるはずです。client2クラスには依存関係がないので何も宣言する必要はありませんが、パッケージは定義する必要があります。
では、有効な依存関係をいくつか追加し、これらの依存関係が許されるように設計を変更してみましょう。まず、先ほど示した設計ファイルの行を次のように変更します。
<package name="client2" package="biz.xsoftware.client2"> <depends>phoneApi</depends> </package>
次に、「Client.java」ファイルをbiz.xsoftware.impl.clientフォルダから「biz.xsoftware.client2」フォルダにコピーします。このクラスはPhoneApiパッケージに依存します。こうすると、ビルドは正常に実行されるようになります。
