asato <asato@ncfreak.com>
最終更新日 : 2002/9/26 (2002/9/26 より)
Template Method パターン
AspectJ を使った Template Method に関するテクニックの種類
フックメソッドの導入
問題: ある目的を満たすのに適切に見えるクラスが存在する。しかし、そのクラスのあるメソッドのアルゴリズム中のあるのステップの存在が目的を満たす邪魔をしている。もし、そのクラスが予めそのステップをサブクラスでオーバーライドできるような柔軟性のある設計を行っていたのであれば目的を満たすことができるのだが、そうはなっていない。
適用可能性:
サンプルコード (実装方法 1 を使った例):
package dp.tm; public class AbstractClass { public void templateMethod() { printString("aaa"); printInt(100); printChar('z'); } private void printString(String s) { System.out.println( s ); } private void printInt(int i) { System.out.println( i ); } private void printChar(char c) { System.out.println( c ); } }
package dp.tm; privileged aspect AbstractClassAspect { pointcut printInt(AbstractClass clazz, int i) : withincode( void AbstractClass.templateMethod() ) && this(clazz) && args(i) && call( void AbstractClass.printInt(int) ); void around(AbstractClass clazz, int i) : printInt(clazz, i) { clazz.printInteger(i); } public void AbstractClass.printInteger(int i) { this.printInt(i); } }
package dp; import dp.tm.*; public class Client { public static void main(String[] args) { AbstractClass abstractClazz = new AbstractClass(); abstractClazz.templateMethod(); } public static class MyAbstractClass extends AbstractClass { public void printInteger(int i) { System.out.println("integer: " + i); } } public static aspect ClientAspect { private static boolean doAspect = true; pointcut newAbstractClass() : if (doAspect == true) && call( AbstractClass.new() ); AbstractClass around() : newAbstractClass() { return new MyAbstractClass(); } } }
aaa integer: 100 // doAspect == false なら "10" が出力 zサンプルコード (実装方法 2 を使った例):
package dp.tm; public class AbstractClass { public void templateMethod() { printString("aaa"); printInt(100); printChar('z'); } private void printString(String s) { System.out.println( s ); } private void printInt(int i) { System.out.println( i ); } private void printChar(char c) { System.out.println( c ); } }
package dp.tm; public abstract class AbstractAbstractClass extends AbstractClass { protected abstract void printInteger(int i); private static aspect InnerAspect { pointcut printInt(AbstractAbstractClass clazz, int i) : withincode( void AbstractClass.templateMethod() ) && this(clazz) && args(i) && call( void AbstractClass.printInt(int) ); void around(AbstractAbstractClass clazz, int i) : printInt(clazz, i) { clazz.printInteger(i); } } }
package dp; import dp.tm.*; public class Client { public static void main(String[] args) { AbstractClass abstractClazz = new AbstractClass(); abstractClazz.templateMethod(); } public static class MyAbstractClass extends AbstractAbstractClass { protected void printInteger(int i) { System.out.println("integer: " + i); } } public static aspect ClientAspect { private static boolean doAspect = true; pointcut newAbstractClass() : if (doAspect == true) && call( AbstractClass.new() ); AbstractClass around() : newAbstractClass() { return new MyAbstractClass(); } } }
aaa integer: 100 // doAspect == false なら "10" が出力 zサンプルコード (実装方法 3 を使った例):
package dp.tm; public class AbstractClass { public void templateMethod() { printString("aaa"); printInt(100); printChar('z'); } private void printString(String s) { System.out.println( s ); } private void printInt(int i) { System.out.println( i ); } private void printChar(char c) { System.out.println( c ); } }
package dp.tm; public abstract class AbstractAbstractClass { private AbstractClass abstractClass = new AbstractClass(); protected abstract void printInteger(int i); public void templateMethod() { abstractClass.templateMethod(); } private static aspect InnerAspect { pointcut callTemplateMethod(AbstractAbstractClass clazz) : this(clazz) && call( void AbstractClass.templateMethod() ); pointcut callPrintInt(AbstractAbstractClass clazz, int i) : cflow( callTemplateMethod(clazz) ) && args(i) && call( void AbstractClass.printInt(int) ); void around(AbstractAbstractClass clazz, int i) : callPrintInt(clazz, i) { clazz.printInteger(i); } } }
package dp; import dp.tm.*; public class Client { public static void main(String[] args) { AbstractAbstractClass abstractClazz = new MyAbstractClass(); abstractClazz.templateMethod(); } public static class MyAbstractClass extends AbstractAbstractClass { protected void printInteger(int i) { System.out.println("integer: " + i); } } }
妥協点:
必要以上のメソッドをクライアントにさらすのを避けるために、次のアプローチがある: カスタム・コンパイル・エラー。AspectJ の提供する機能の 1 つである declare error を使うことで、クライアントがフックメソッドを呼んでいるようなコードが存在するときに、コンパイル時にエラーとなるような方法が可能となる。
package dp.tm; privileged aspect AbstractClassAspect { ... pointcut illegalPrintIntegerMethodCall() : !within(AbstractClass || AbstractClassAspect) && call(void AbstractClass.printInteger(int) ); declare error : illegalPrintIntegerMethodCall() : "!within(AbstractClass || AbstractClassAspect)"; }