最終更新日 : 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