最終更新日 : 2003/3/30
asato <asato@ncfreak.com>
静的 Decorator
実装
public class StringComponent {
public void print(String s) {
System.out.println(s);
}
}
public aspect ConcreteDecoratorA {
declare precedence: ConcreteDecoratorA, ConcreteDecoratorB;
protected pointcut printCall(String s):
call(public void StringComponent.print(String)) && args(s);
void around(String s): printCall(s) {
s = "*** " + s + " ***";
proceed(s);
}
}
public aspect ConcreteDecoratorB {
protected pointcut printCall(String s):
call(public void StringComponent.print(String)) && args(s);
void around(String s): printCall(s) {
s = "[ " + s + " ]";
proceed(s);
}
}
public class Client {
public static void main(String[] args) {
StringComponent comp = new StringComponent();
comp.print("aaa"); // [ *** aaa *** ]
}
}
[ *** aaa *** ]
疑問
Decorator パターンであると言えるのか?
静的 Decorator が柔軟性を発揮できる場所は、コンパイル前に precedence 宣言を使うことにより、装飾の順番を変えることぐらいです:
public class StringComponent {
// ... 変更なし
}
public aspect ConcreteDecoratorA {
// 装飾の順番を変更
declare precedence: ConcreteDecoratorB, ConcreteDecoratorA;
// ... 変更なし
}
public aspect ConcreteDecoratorB {
// ... 変更なし
}
public class Client {
public static void main(String[] args) {
StringComponent comp = new StringComponent();
comp.print("aaa"); // *** [ aaa ] ***
}
}
*** [ aaa ] ***しかし、それに伴って次に思いつく疑問は、実践的にそのようなシチュエーションはありえるのか? というものです。
実践的に使えるのか?
public class StringComponent {
public void print(String s) {
System.out.println("*** [ " + s + " ] ***");
}
}
ここで注意する必要があるのは、まず、実装例自体が非常に単純なものである、ということです。したがって、わざわざアスペクトを導入するまでもない、という結論にも至りえます。
もう 1 つは、ConcreteComponent クラス (StringComponent) の作者と ConcreteDecorator (ConcreteDecoratorA と ConcreteDecoratorB) の作者が、同じであり、静的 Decorator を使うかどうかのどちらかをとらなければいけない、と仮定していることです。これは、起こりえる唯一の状況ではありません。
具体的には、以下のような状況が考えられます:
状況例
package a_lib;
public class StringComponent {
public void print(String s) {
System.out.println(s);
}
}
package a_lib;
public class Main {
public static void main(String[] args) {
StringComponent comp = new StringComponent();
comp.print("aaa"); // aaa
}
}
<project name="decorator" default="compile" > <target name="compile" > <mkdir dir="build" /> <javac destdir="build" srcdir="src"/> <jar jarfile="a_lib.jar" basedir="build" /> </target> </project>
a_lib>java -cp a_lib.jar a_lib.Main aaaB さん: アプリケーション開発者
package b_app;
import a_lib.StringComponent;
public class Main {
public static void main(String[] args) {
StringComponent comp = new StringComponent();
comp.print("aaa"); // *** [ aaa ] ***
}
}
package b_app;
import a_lib.StringComponent;
public aspect ConcreteDecoratorA {
declare precedence: ConcreteDecoratorB, ConcreteDecoratorA;
protected pointcut printCall(String s):
call(public void StringComponent.print(String)) && args(s);
void around(String s): printCall(s) {
s = "*** " + s + " ***";
proceed(s);
}
}
package b_app;
import a_lib.StringComponent;
public aspect ConcreteDecoratorB {
protected pointcut printCall(String s):
call(public void StringComponent.print(String)) && args(s);
void around(String s): printCall(s) {
s = "[ " + s + " ]";
proceed(s);
}
}
ajc -verbose -injars ../a_lib/a_lib.jar -sourceroots src -outjar b_app.jar
b_app>java -cp ../../lib/aspectjrt.jar;b_app.jar b_app.Main *** [ aaa ] ***考察
この例から見て取れることは、例えば、以下のものです:
1 つの方法は、ConcreteDecoratorA と ConcreteDecoratorB の間に共通の抽象アスペクトを導入することです:
package b_app;
import a_lib.StringComponent;
public abstract aspect AbstractDecorator {
protected pointcut printCall(String s):
call(public void StringComponent.print(String)) && args(s);
}
package b_app;
public aspect ConcreteDecoratorA extends AbstractDecorator {
declare precedence: ConcreteDecoratorB, ConcreteDecoratorA;
void around(String s): printCall(s) {
s = "*** " + s + " ***";
proceed(s);
}
}
package b_app;
public aspect ConcreteDecoratorB extends AbstractDecorator {
void around(String s): printCall(s) {
s = "[ " + s + " ]";
proceed(s);
}
}
もう 1 つの方法は、アスペクトの継承を行わない方法です:
package b_app;
import a_lib.StringComponent;
public aspect StringComponentAspect {
public pointcut printCall(String s):
call(public void StringComponent.print(String)) && args(s);
}
package b_app;
public aspect ConcreteDecoratorA {
declare precedence: ConcreteDecoratorB, ConcreteDecoratorA;
void around(String s): StringComponentAspect.printCall(s) {
s = "*** " + s + " ***";
proceed(s);
}
}
package b_app;
public aspect ConcreteDecoratorB {
void around(String s): StringComponentAspect.printCall(s) {
s = "[ " + s + " ]";
proceed(s);
}
}
その他の実装方法
場合によっては、以下のような実装も適切かもしれません:
package b_app;
import a_lib.StringComponent;
public aspect StringComponentDecorator {
declare precedence: ConcreteDecoratorA, ConcreteDecoratorB;
private pointcut printCall(String s):
call(public void StringComponent.print(String)) && args(s);
private static aspect ConcreteDecoratorA {
void around(String s): printCall(s) {
s = "*** " + s + " ***";
proceed(s);
}
}
private static aspect ConcreteDecoratorB {
void around(String s): printCall(s) {
s = "[ " + s + " ]";
proceed(s);
}
}
}
あるいは単に:
package b_app;
import a_lib.StringComponent;
public aspect StringComponentDecorator {
private pointcut printCall(String s):
call(public void StringComponent.print(String)) && args(s);
void around(String s): printCall(s) {
s = "[ " + s + " ]";
proceed(s);
}
void around(String s): printCall(s) {
s = "*** " + s + " ***";
proceed(s);
}
}
ただし、この場合は、どの順番で advice が呼び出されるかによってオブジェクトの動作が変わりえます (実際に、この場合では、上から順に advice が実行されます)。
参考文献とリソース
更新履歴
todo