SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

Verifydesignを使って設計の依存関係を監視・強制する

フリーのツールによる好ましくないクラス依存関係の検出

ダウンロード ソースコード (1.3 MB)

望ましくない依存関係を見つける

 最初に、最も基本的なルール違反として、定義済みAPIを迂回しようとしたときにどうなるかをお見せします。ファイル「verifydesign/packagedesign/input/javasrc/biz/xsoftware/impl/client/Client.java」を開き、createPhoneTheWrongWay()というメソッドに以下のコードを追加してください。

PhoneImpl phone = new PhoneImpl();

 これによってソースコードにどのような変更が加えられたかを図4に示します。赤い線は、付け加えられた依存関係を表します。これは現在実装されている設計であり、設計ファイルに定義されている望ましい「パッケージ設計」ではないことに注意してください。

図4 依存関係の望ましくない変更。設計に従うならば、クライアントが依存できるのはphoneApiのみである。Verifydesignはこのミスを検出する
図4 依存関係の望ましくない変更。設計に従うならば、クライアントが依存できるのはphoneApiのみである。Verifydesignはこのミスを検出する

 この変更の後で、ビルドを再度実行します。ビルドには、同じコマンドを使います。

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に示します。

図5 良好なカプセル化。クライアントとphoneApiはrmiモジュールについて関知しない。rmiの存在を知るのは、PhoneImplコンポーネント(phoneApiの実装)だけである
図5 良好なカプセル化。クライアントとphoneApiはrmiモジュールについて関知しない。rmiの存在を知るのは、PhoneImplコンポーネント(phoneApiの実装)だけである

 これは最適なカプセル化です。クライアントと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のようになります。これもまた、設計ファイルに定義されている「パッケージ設計」とは違います。

図6 新しいClient2パッケージ。2番目のクライアントパッケージを追加すると、依存関係の状況はこのようになる
図6 新しいClient2パッケージ。2番目のクライアントパッケージを追加すると、依存関係の状況はこのようになる

 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パッケージに依存します。こうすると、ビルドは正常に実行されるようになります。

次のページ
古くなったドキュメント

この記事は参考になりましたか?

japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Dean Hiller(Dean Hiller)

VerifydesignおよびMocklibの作者。アーキテクチャ担当ディレクタとして、Verifydesignなどのツールを駆使してアーキテクチャの変遷を監視し、アジャイルプロセスを厳守するための規律をチームに植え付ける。指揮下のチームでは、製品のコードの75%以上が自動テストによりテストされる。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1112 2007/03/26 07:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー