本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

AOPを使いコントラクトを実施する

Design by Contract(DBC)をAspectJと共にJavaソフトウェア開発に応用

Filippo Diotalevi, IT Specialist, IBM Italy
Author photo
Filippo DiotaleviはミラノにあるIBM ItalyのITスペシャリスト(IT専門家)であり、そこでは主にJ2EEアプリケーション開発者として勤務します。彼が関心を抱く分野は、パターン、アスペクト指向プログラミング、そしてアジャイル方法論です。IBM Redbook「Patterns: Direct Connections for Intra- and Inter-enterprise」の共著者、Webサイトそして雑誌で発行された数多くの技術記事の著者、そしてJava User Group Milanoの創立者でもあります。

概要: 企業用ソフトウェアを開発する場合において、Javaコードは外部コンポーネントとの相互作用を頻繁に迫られます。アプリケーションがレガシー・アプリケーション、外部のビジネス・システム、またはサード・パーティーのライブラリーと通信しなくてはならない場合、制御できないコンポーネントの使用は予測不可能な結果と言うリスクと遭遇することにもなりかねません。IBMのIT スペシャリストであるFilippo Diotalevi は、コードをすっきりとしていて柔軟な状態に保ちながらコンポーネント間の明確なコントラクトを設計・定義することを促すことにより、どのようにしてAOP(aspect-oriented programming:アスペクト指向プログラミング)がこのリスクを緩和するかを論議します。

日付:  2004年 7月 15日
レベル:  初級 この記事の原文:  英語
アクティビティー: 1328 ビュー
お気軽にご意見・ご感想をお寄せください: 


Design by Contract(DBC) とは、ソフトウェアの品質、信頼性、そして再利用性を確証することを目的とするオブジェクト指向のソフトウェア設計のテクニックです。DBCの重要な鍵を握る概念は、下記の方法でその目的を達成できることです。

  • 可能な限りコンポーネント間の通信を正確に指定する
  • 通信プロセスの予測される結果と相互の義務を定義する

これらの相互の義務はコントラクト(contracts)と呼ばれ、アプリケーションがコントラクトに準拠するかをチェックするのにアサーション(assertions)を使います。簡単に言えば、アサーションはプログラムの実行のとある時点で挿入されるtrue でなくてはならないブール表記です。アサーションの失敗はソフトウェアのバグによく見られる症状ですので、それはユーザーに報告されなくてはなりません。

外部コンポーネントかライブラリーを取り扱い、(アプリケーションがそれらに受け渡しそれらから受け取る)データが正確であることを保証するときに、DBCは特に貴重です。この記事は、DBCを実装するためにAOPを採用する抽象インフラストラクチャー、それから外部コンポーネントとのコントラクトを確立するサンプル・アプリケーションを紹介します。

アサーションそしてJava言語

DBCは3種類のアサーションを識別します。

  • 事前条件: 外部コンポーネントを正しく呼び出すためにクライアントが満たさなくてはならない義務。
  • 事後条件: 外部コンポーネントの実行後に予測される結果。
  • インバリアント: 外部コンポーネントの実行後も不変であり続ける条件。

DBCの王者

Bertrand Meyer氏著の「 Object Oriented Software Construction」(参考文献を参照)は、Design by Contractを定義し形式化します。確固として信頼性の高いオブジェクト指向コードを書くうえでの中心的で体系的なアプローチとしてDBCの採用を力説します。アサーションへの良質のサポートを提供するEiffelプログラミング言語を、彼は発明しました。「Design by Contract」は、Meyer氏がEiffelを開発そして分散するために共同設立した会社であるInteractive Software Engineeringの登録商標です。

Java言語は昔からアサーションへのネイティブのサポートを供給していたわけではありません。assertの記述はバージョン1.4に追加されています。しかしながら、常日頃から見かけるようなコーディングでDBCを応用するのは大変です。実を言えば、最も一般的な(アプリケーション・コードに事前/事後条件アサーションを直接使用する)アプローチはコードのモジュール性そして再利用性の面で深刻な障害を抱えます。このアプローチはもつれたコードの鮮明な例です。それはアサーションが必要とする非機能的なコードにビジネス・ロジックのコードを混合します。アプリケーション・コードを変更せずしてアサーションを変えたり取り除いたり出来ませんので、そのコードは柔軟性に欠けます。

この問題の理想的な解決法は以下の4つの必須条件を満たします。

  • 透過性: 事前条件と事後条件のコードがビジネス・ロジックと混同していない。
  • 再利用性: 解決法の大半の部分は再利用可能。
  • 柔軟性: アサーションモジュールを簡単なやり方で追加、削除、そして修正可能。
  • 単一性: 単純な構文を使ってアサーションを指定可能。

透過的DBCにAOPを使用

もしも懸念からの離脱、透過性、そして柔軟性に狙いを定めているのでしたら、多くの場合AOP(aspect-oriented programming)が正しい解決策をもたらします。(アプリケーションの多岐に渡るモジュールにたびたび含まれ、ある意味ではアプリケーションのコードと混合される、普通に使われる機能である)事前条件、事後条件、そしてインバリアントはクロスカッティングする懸念です。別々のモジュールにこれらの機能をコーディングしそれらを柔軟性に富み宣言する方法で応用することを開発者に提供するのが、AOPの目標です。

この記事は読者がAspectJ のもとにあるAOPに総合的に精通していることを想定し、AOPの入門書を目指すわけではありません。このトピックの入門記事のリストは、参考文献の章にあります。

実装インフラストラクチャー

図1にて図解されているとおり、前述の4つの必須条件を満たす解決法は3つの部分から成ります。

  • (DBCとは関係の無いエレメントを含む)アプリケーション・コード
  • (事前条件、事後条件、そしてインバリアントのチェック付きの)コントラクト実装
  • コードとコントラクトの間の橋渡しを演出し、正しいロジックとともにコードの正しい部分にコントラクトをほどこすオブジェクト

図1. DBCの正しくモジュラー化された解決法
DBCの正しくモジュラー化された解決法

コントラクト実装またはアプリケーション・コードの変更無しでコントラクトの応用そして除去を可能にする柔軟性の高い解決法を、図1にて図解される仕組みは保証します。そして、それはアプリケーションに対する完全な透過性を保ちます。

実装詳細

コントラクトは特定のインターフェースを実装するJavaクラスであり、「bridge(ブリッジ)」はAspectJのアスペクトです。このアスペクトはコントラクトが応用される的確なポイントそしてコントラクトを応用するのに必要な制御ロジックを指定します(図2参照)。


図2. アクティビティー図として表現されるコントラクト・ロジックによる設計
アクティビティー図として表現されるコントラクト・ロジックによる設計

図2にて図解されるロジックは全てのコントラクトに共通しますので、それを指定する共通の抽象的なアスペクトを作成できます。クラスそしてアスペクトのダイアグラムを図3にて示します。


図3. コントラクトをチェックするシステムの基本的なコンポーネント
コントラクトをチェックするシステムの基本的なコンポーネント

図2のアクティビティー図にて宣言される制御ロジックを、AbstractContract アスペクトは指定します。(ContractManagerインターフェースの実装に定義される)コントラクトが応用されるプログラムの実行のとある部分を、それは表現しません(つまり、抽象的なままです)。AbstractContractアスペクトを拡張するConcreteContractアスペクトは、下記の項目を示す責任を抱きます。

  • TargetPointcutのポイントカットを介するコントラクト・チェックの実行の的確なポイント
  • getContractManager()メソッドを介するコントラクト・チェックに責任を持つクラス

アプリケーションと外部モジュールの間にあるコントラクトをチェックするうえで必要なコードを含むクラスは、ContractManager インターフェースを実装します。リスト1に示されるContractManagerは、コントラクトをチェックするクラスの基本的な振る舞いを定義する単純なJavaインターフェースです。


リスト1. ContractManager インターフェース
                
public interface ContractManager 
{
   /**
   * Check the preconditions
   */
   public void checkPreConditions(Object thisObject, Object[] args)
      throws ContractBrokeException;
 
   /**
   * Check the postconditions
   */		 
   public void checkPostConditions(Object thisObject, Object returnValue, Object[] args)
      throws ContractBrokeException;

   /**
   * Check the invariants
   */
   public void checkInvariants(Object thisObject) throws ContractBrokeException;
}

チェックを入れたいそれぞれの種類のアサーションへの異なるメソッドをContractManagerインターフェースは定義します。それぞれのメソッドは、thisObject パラメーターを介す形で、(コントラクトが確証される予定の機能を呼び出す)Javaオブジェクトへのアクセスを抱きます。事前条件と事後条件のメソッドも機能の引き数(args)として受け渡される値を確認することができます。事後条件のメソッドのみがreturnValueパラメーターを介して(結果として得られた)戻り値を受け取ります。これら3つのメソッドを組み合わせて使用することにより、ほとんど全ての共通の条件をチェックできます。

AbstractContract アスペクトはコントラクトのチェックに必要な制御ロジックを実行します。このロジックはaround():targetPointcut()のアドバイスと言う形で表現されます。リスト2はAbstractContractのアスペクトを示します。


リスト2. AbstractContractのアスペクト
                
public abstract aspect AbstractContract 
{
   /**
   * Define the pointcut to apply the contract checking
   * MUST CONTAIN A METHOD CALL
   */
   public abstract pointcut targetPointcut();

   /**
   * Define the ContractManager interface implementor to be used
   */
   public abstract ContractManager getContractManager();

   /**
   * Perform the logic necessary to perform contract checking
   */
   Object around(): targetPointcut()
   {
      ContractManager cManager = getContractManager();
      System.out.println("Checking contract using:" + 
        cManager.getClass().getName());

      if (cManager!=null)
      {
         System.out.println("Performing initial invariants check");
         cManager.checkInvariants(thisJoinPoint.getTarget());
      }
      if (cManager!=null)
      {
         System.out.println("Performing pre-conditions check");
         cManager.checkPreConditions(thisJoinPoint.getTarget(), thisJoinPoint.getArgs());
      }

      Object obj = proceed();

      if (cManager!=null)
      {
         System.out.println("Performing post conditions check");
         cManager.checkPostConditions(thisJoinPoint.getTarget(), 
           obj, thisJoinPoint.getArgs());
      }
      if (cManager!=null)
      {
         System.out.println("Performing final invariants check");
         cManager.checkInvariants(thisJoinPoint.getTarget());
      }
      return obj;
   }
}

具体的なコントラクト・チェッカーのアスペクトを実装するときにオーバーライドされるべき2つの抽象メソッドを、AbstractContract アスペクトはもたらします。

  • public abstract pointcut targetPointcut()は、アドバイスが適用されるべきポイントカットを表記します。ポイントカットはメソッド呼び出しでなくてはなりません。
  • public abstract ContractManager getContractManager()は、正確なコントラクト・チェックを実装するContractManager のインスタンスを戻さなくてはなりません。

インバリアントなチェックが二度(両方ともサービス実行の前後にて)実行されていることに注目するのが重要です。サービスの実行が一部の外部フィールドの値に影響を及ぼさないことのチェックを可能にします。

コントラクトの失敗は、アドバイスの実行を停止するContractBrokeExceptionを引き起こします。


実際のコントラクト・チェック

AOPでDBCを実装するために必要なインフラストラクチャーを理解するに到りましたので、それを実際に実行する段階に入ったと言えるでしょう。顧客データを回収するために外部のCRM(Customer Relationship Management - 顧客関係管理)システムを照会する必要があるとします。CRMシステムの呼び出しは、以下のとおりになる可能性があります。

Customer cus = companyCustomerSystem.getCustomer("Pluto");

開発者の観点から見れば、getCustomer は外部コンポーネントですので、getCustomer機能の実装は無意味です。しかし、それが不正な結果を戻さないことをチェックするのは非常に重要です。アプリケーションがCRMシステムへ間違っているかまたは無意味な入力を受け渡さないことを確証するのも同様に重要です。AbstractContractを拡張する具体的なアスペクトを作り上げることにより、どちらの偶発事態にも対処できます。具体的なアスペクトは2つのメソッドをオーバーライドします。

  • targetPointcut(): ポイントカットを定義し、コントラクト・チェックを応用する場所を指示する
  • getContractManager(): 全てのチェックの実行に対する責任を持つContractManager実装を定義する

リスト3は、サンプル・アプリケーションの具体的なアスペクトを示します。


リスト3. 具体的なコントラクトのアスペクト
                
public aspect CcCompanySystem extends AbstractContract
{
   public pointcut targetPointcut(): call(Customer CompanySystem.getCustomer(String));

   public ContractManager getContractManager()
   {
      return new CompanySystemContractManager();
   }
}

CompanySystemクラスのgetCustomerメソッドの呼び出しに表記されるポイントカットへCompanySystemContractManager と呼ばれるコントラクト・チェッカーが呼び出されることを、CcCompanySystem アスペクトは指定します。コントラクト・チェックの操作の制御ロジックを定義する必要はありません。なぜならば、それは上位の抽象的なアスペクトAbstractContractリスト2)から継承されているからです。

最後のステップは、コントラクト・チェックをさせるためにJavaクラスを作成することです。以前に述べたとおり、このクラスはContractManagerインターフェースを実装しなくてはなりません。リスト4はサンプル・クラスCompanySystemContractManagerを表示します。


リスト4. サンプル・アプリケーション用のContractManager実装
                
public class CompanySystemContractManager implements ContractManager 
{		 
   /**
   * Check preconditions
   */
   public void checkPreConditions(Object thisObject, Object[] args) 
      throws ContractBrokeException 
   {
      Object arg = args[0];
      if (arg == null)
      {
         throw new ContractBrokeException("PRECONDITION ERROR: " +
            " Argument of getCustomer shouldn't be null");
      }
   }

   /**
   * Check postconditions
   */
   public void checkPostConditions(Object thisObject, Object value, Object[] args) 
      throws ContractBrokeException 
   {
      if (value == null)
      {
         throw new ContractBrokeException("POSTCONDITION ERROR: " +
            " Return value of getCustomer shouldn't be null");
      }		 		 
   }

   /**
   * Check invariants
   */
   public void checkInvariants(Object thisObject) throws ContractBrokeException
   {
         //invariants check
   }
}

引き数か戻り値がnullの場合のみにおいて、リスト4のCompanySystemContractManagerクラスはチェックしますが、極端に洗練されたチェックを追加するためにそれを拡張することも可能です。

注意すべき重要なポイント:全てのコントラクト・チェックは1つのCompanySystemContractManagerオブジェクトをインスタンス化しますので、最初のインバリアント・チェックの途中で専用フィールドにデータを保管し、CRMシステム呼び出しの実行後にそれらが変更されていないことを検査することにより、インバリアントをチェックできます。

アプリケーションとCRMシステムの間で簡単なコントラクトを確立させられるのは、非常に喜ばしいことです。AspectJコンパイラーを使用してアプリケーションをコンパイルした後、コントラクトはCompanySystemクラスのgetCustomerメソッドの全ての呼び出しに応用され、それとのアプリケーションの相互作用の整合性をチェックします。さらに、CompanySystemContractManager が十分に汎用化されていれば、再利用が可能です。別のコントラクト・チェックに応用するには、targetPointcut のみを再定義すればよいだけです。

サンプルの解決法は、この記事の冒頭でリストした条件を完全に満たします。

  • ビジネス・ロジックのコードはコントラクト・チェックへの参照を含みませんので透過性があります。
  • それは簡単なインフラストラクチャー(インターフェースと抽象的なアスペクト)に頼り、複数の状況で単独のContractManagerの再利用を促しますので再利用性を持ちます。
  • どのアスペクトを応用しそしてつまりどのコントラクトをチェックしたいかを選ぶためにAspectJ のコンパイラー機能を使用できますので柔軟性に富みます。
  • いくつかのクラスのみから成り立っていますので単純です。

まとめ

この記事ではAspectJ とAOP のテクニックを活用してJavaアプリケーション開発にDBCを採用する可能なアプローチを説明しました。提案される解決策はすっきりとしていて柔軟な解決法を保証します。なぜならば、(ビジネス・ロジックから別のところでコントラクトをコーディングし宣言する形でそれらを応用させる)極端に単純でうまくモジュラー化された設計に、解決策が信頼を寄せるからです。


参考文献

著者について

Author photo

Filippo DiotaleviはミラノにあるIBM ItalyのITスペシャリスト(IT専門家)であり、そこでは主にJ2EEアプリケーション開発者として勤務します。彼が関心を抱く分野は、パターン、アスペクト指向プログラミング、そしてアジャイル方法論です。IBM Redbook「Patterns: Direct Connections for Intra- and Inter-enterprise」の共著者、Webサイトそして雑誌で発行された数多くの技術記事の著者、そしてJava User Group Milanoの創立者でもあります。

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=219197
ArticleTitle=AOPを使いコントラクトを実施する
publish-date=07152004
author1-email=filippo.diotalevi@it.ibm.com
author1-email-cc=

タグ

Help
このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。

スライダーバーを使用することで、より多く(少なく)タグを表示します。

人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。

マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。

このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。