デコレータはメソッドの振る舞いを変更することができる仕組みである。 本クラスと同じインタフェースを実装し、@Decoratorアノテーションなどを付与すると、本クラスの処理がデコレータに委譲される。 委譲されることにより、本メソッドの呼び出し前後に処理をかますこともできるし、メソッドの振る舞いを変更することもできる。
デコレータの考え方はインターセプタに似ている。 デコレータもインターセプタも本メソッドを呼び出す前後に処理を挿入することができる機能である。 但し、両者の目的は異なる。インターセプタがログ出力、トランザクション制御、認証など業務処理に関係ない処理を横断的に挿入することを目的としているのに対し、 デコレータは業務的な処理を拡張することを目的としている。
インターセプタは利用する側にアノテーションの付与が必要だが、デコレータは本クラスを全く変更せずに使用することができる。よって、恒久的な対応よりも、一時的に処理を変更したい場合に向いている。
簡単なサンプルを使って説明する。
サンプルのための管理ビーンを作成する。通常の手順で作成し、特別なことは何もしない。 なお、send()ではロジッククラスのcalculate()の戻り値を標準出力している。
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を返却する。
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();
}
デコレータクラスを作成する。
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_BEFORE | 0 | JavaEEのインターセプタ |
LIBRARY_BEFORE | 1000 | 拡張ライブラリのインターセプタ |
APPLICATION | 2000 | アプリケーションのインターセプタ |
LIBRARY_AFTER | 3000 | アプリケーションの後に実行する拡張ライブラリのインターセプタ |
PLATFORM_AFTER | 4000 | アプリケーションの後に実行するJavaEEのインターセプタ |
なお、TestLogicDecoratorはTestLogicと同じインタフェース(TestLogicIF)をインプリメントする。
②ロジックインタフェースをインジェクト
@Inject
@Delegate
TestLogicIF logic;
TestLogicIFの変数を用意して、@Injectを付与する。また@Delegateアノテーションも付与する。 これで具象クラスであるTestLogicがlogic変数にインジェクトされ、処理も委譲される。(「Delegate」は「委譲する」という意味)
③calculateメソッドを実装
public int calculate(){
// 実メソッド呼び出し
int ret = logic.calculate();
// 10倍にして返却
return ret * 10;
}
デコレータ自身もTestLogicIFをインプリメントしているため、calculateメソッドを実装する。 ここで注目すべきは本体であるTestLogicのcalculateメソッドを呼び出している点である。 インターセプタのproceedメソッドのように、本体の呼び出しはデコレータが行う。 その前後に処理を入れることにより、本メソッドの振る舞いを変更(拡張)することができる。 ここではTestLogicで返却された値(100)に10を掛けて、値を10倍にしている。
なお、DecoratorTestBeanのsendメソッド実行時の出力結果は以下となる。(TestLogic#calculateの戻り値が10倍されている)
ret : 1000