N2 ToolBox(跡地)

跡地です。引っ越しました。http://d.hatena.ne.jp/nosen

AspectJでDbC(サンプル2)

2004-07-04 23:46:41 | 開発手法/方法論
前回紹介したコントラクトをもとにStackインターフェースを実装したコードを紹介します。
package sample.dbc;

import java.util.ArrayList;

/**
 * Stack.
 */
public class StackImpl implements Stack {
    private ArrayList stack = new ArrayList();

    public int size() {
        return stack.size();
    }

    public void push(Object o) {
        stack.add(o);
    }

    public Object pop() {
        int idx = stack.size() - 1;
        //ポイント1
        if(idx <0) {
あらかじめコントラクトが決まっていることで、ポイント1のような箇所でNullを返すのか例外を投げるのかといったありがちな実装上の迷いがなくなります。次に、JUnitのテストケースを一部ですが紹介します。
    /**
     * testPop2.
     */
    public void testPop3() {
        Stack s = new StackImpl();
        s.push(new String("foo"));
        s.push(new String("bar"));
        s.pop();
        s.pop();
        s.pop();
    }

見て分かる通り、アサーションのコードが一切ありませんね。
このテストケースはあらかじめ前回紹介したAspectJによるコントラクトがWeavingされていることが前提となっています。ですから、実際にはStackImplの呼び出しの前後に事前条件と事後条件のチェックのコードが挿入されることになります。

今回紹介したのは、本当にシンプルなケースです。現実に運用されるシステムはもっと複雑ですし、開発者のスキルの問題などもありますから、実際の開発にAspectJによるDbCを適用するには、もっといろいろなノウハウを蓄積していく必要があると思います。

AspectJでDbC(サンプル)

2004-07-02 00:25:26 | 開発手法/方法論
今日は、AspectJでDbCを実際にちょろっとやってみたサンプルを紹介したいと思います。サンプルとして簡単なスタックの実装を考えてみました。
以下のようなインターフェースがあるとします。

package sample.dbc;

public interface Stack {
    /**
     * スタックのサイズを返す.
     */
    int size();
    /**
     * スタックに引数のオブジェクトをPushする.
     * @param o Pushするオブジェクト
     */
    void push(Object o);
    /**
     * スタックからオブジェクトをpopする.
     */
    Object pop();
    /**
     * スタックの一番上のオブジェクトを返す.
     */
    Object peek();
}
特に説明する必要も無いですね。
このインターフェースに対するContractをAspectJで記述すると以下のようになります。

package sample.dbc;

import junit.framework.Assert;

public aspect StackContract {
    declare parents: StackContract extends Assert;

    /**
     * Invariant.
     */
    after(Stack s) : !within(StackContract) && target(s) && call(public * Stack.*(..)) {
        assertTrue(s.size() >= 0);
    }

    /**
     * Stack#push.
     */
    void around(Stack s, Object o) : target(s) && args(o) && call(public void Stack.push(Object)) {
        //@pre
        assertNotNull(o);

        //@post
        int oldsize = s.size();
        proceed(s, o);
        assertEquals(oldsize + 1, s.size());
        assertSame(o, s.peek());

    }

    /**
     * Stack#pop.
     */
    Object around(Stack s) : target(s) && call(public void pop()) {
        //@post
        int oldsize = s.size();
        Object oldpeek = s.peek();
        Object rv = proceed(s);
        if(oldsize > 0) {
            assertSame(oldpeek, rv);
            assertEquals(oldsize - 1, s.size());
        } else {
            assertNull(rv);
            assertEquals(0, s.size());
        }
        return rv;
    }
}

事前条件、事後条件のチェックをJUnitのAssertクラスを使って行っています。ですから、このアスペクトをWeavingしてテストすれば、JUnitのテストケースにはアサーションのコードを一切書かなくて良くなります。
JUnitでは一般的に一つ一つの入力データに対する具体的な結果を検証するのに対し、DbCでは一般的なルールとして記述するので、記述自体の難易度はDbCの方が難しいです。しかし、クラスの仕様としての明解さ、厳密さはこちらの方が上だと思います。

よく言われるJUnitのメリットの一つに、テストケースからクラスの使い方が分かる、というのがあります。しかし、テストケースからクラスの使い方を知る、という作業は、具体的な入力値と出力値から一般的な仕様を頭の中でリバースエンジニアリングする作業だと思います。DbCにおいては、記述されるのは仕様そのものなので、より直截なアプローチだと思います。

次回は、Stackの実装とJUnitテストケースを紹介します。