技術空間

JavaEE

デコレータでメソッドの振る舞いを変更する


TOP > JavaEE > デコレータでメソッドの振る舞いを変更する



■デコレータとは

デコレータはメソッドの振る舞いを変更することができる仕組みである。 本クラスと同じインタフェースを実装し、@Decoratorアノテーションなどを付与すると、本クラスの処理がデコレータに委譲される。 委譲されることにより、本メソッドの呼び出し前後に処理をかますこともできるし、メソッドの振る舞いを変更することもできる。

デコレータの考え方はインターセプタに似ている。 デコレータもインターセプタも本メソッドを呼び出す前後に処理を挿入することができる機能である。 但し、両者の目的は異なる。インターセプタがログ出力、トランザクション制御、認証など業務処理に関係ない処理を横断的に挿入することを目的としているのに対し、 デコレータは業務的な処理を拡張することを目的としている。

インターセプタは利用する側にアノテーションの付与が必要だが、デコレータは本クラスを全く変更せずに使用することができる。よって、恒久的な対応よりも、一時的に処理を変更したい場合に向いている。

簡単なサンプルを使って説明する。

■サンプル実行
管理ビーンクラスの作成

サンプルのための管理ビーンを作成する。通常の手順で作成し、特別なことは何もしない。 なお、send()ではロジッククラスのcalculate()の戻り値を標準出力している。

DecoratorTestBean.java
package decorator;

import java.io.Serializable;

import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;

/** 管理Beanクラス */
@Named
@ViewScoped
public class DecoratorTestBean implements Serializable{

    /** シリアルバージョンUID */
    private static final long serialVersionUID = 1L;
    
    /** ロジッククラス */
    @Inject
    private TestLogic logic;

    /** イベントメソッド */
    public void send(){
        int ret = logic.calculate();
        System.out.println("ret : " + ret);
    }
}
ロジッククラスとインタフェースの作成

DecoratorTestBeanクラスがインジェクトするクラスTestLogicを作成する。 TestLogicにはTestLogicIFを実装させる。 デコレータを利用するには実クラスがインタフェースを実装していなければならない

なお、TestLogicのcalculate()は数値の100を返却する。

TestLogic.java
package decorator;

import javax.enterprise.context.RequestScoped;

/** ロジッククラス */
@RequestScoped
public class TestLogic implements TestLogicIF{

    /** メソッド */
    public int calculate(){
        return 100;
    }
}
TestLogicIF.java
package decorator;

/** ロジックインタフェース */
public interface TestLogicIF{

    /** メソッド */
    public abstract int calculate();
}
デコレータクラスの作成

デコレータクラスを作成する。

TestLogicDecorator.java
package decorator;

import javax.annotation.Priority;
import javax.decorator.Decorator;
import javax.decorator.Delegate;
import javax.inject.Inject;
import javax.interceptor.Interceptor;

/** ロジッククラス(デコレータ) */
@Decorator
@Priority(Interceptor.Priority.APPLICATION)
public class TestLogicDecorator implements TestLogicIF{

    /** ロジックインタフェース */
    @Inject
    @Delegate
    TestLogicIF logic;

    /** メソッド */
    public int calculate(){
        
        // 実メソッド呼び出し
        int ret = logic.calculate();

        // 10倍にして返却
        return ret * 10;
    }
}
[説明]

①クラス宣言

@Decorator
@Priority(Interceptor.Priority.APPLICATION)
public class TestLogicDecorator implements TestLogicIF{

デコレータを示すものとして@Decoratorアノテーションを付与する。

また@Priorityも付与する。これはインターセプタで付与するものと同じで、デコレータが複数存在した場合の優先度を指定する。 数値が少ないものから順番に実行される。通常は2000~2999の間になるように指定する。以下「インターセプタでメソッドの前後に処理を入れる」から抜粋。

定数名説明
PLATFORM_BEFORE0JavaEEのインターセプタ
LIBRARY_BEFORE1000拡張ライブラリのインターセプタ
APPLICATION2000アプリケーションのインターセプタ
LIBRARY_AFTER3000アプリケーションの後に実行する拡張ライブラリのインターセプタ
PLATFORM_AFTER4000アプリケーションの後に実行するJavaEEのインターセプタ

なお、TestLogicDecoratorTestLogicと同じインタフェース(TestLogicIF)をインプリメントする。


②ロジックインタフェースをインジェクト

@Inject
@Delegate
TestLogicIF logic;

TestLogicIFの変数を用意して、@Injectを付与する。また@Delegateアノテーションも付与する。 これで具象クラスであるTestLogiclogic変数にインジェクトされ、処理も委譲される。(「Delegate」は「委譲する」という意味)


③calculateメソッドを実装

public int calculate(){

    // 実メソッド呼び出し
    int ret = logic.calculate();

    // 10倍にして返却
    return ret * 10;
}

デコレータ自身もTestLogicIFをインプリメントしているため、calculateメソッドを実装する。 ここで注目すべきは本体であるTestLogicのcalculateメソッドを呼び出している点である。 インターセプタのproceedメソッドのように、本体の呼び出しはデコレータが行う。 その前後に処理を入れることにより、本メソッドの振る舞いを変更(拡張)することができる。 ここではTestLogicで返却された値(100)に10を掛けて、値を10倍にしている。

なお、DecoratorTestBeansendメソッド実行時の出力結果は以下となる。(TestLogic#calculateの戻り値が10倍されている)

出力結果
ret : 1000
- JavaEEの入門本 -



TOP > JavaEE > デコレータでメソッドの振る舞いを変更する

Tweet ̃Gg[͂ĂȃubN}[Nɒlj
技術空間