AspectJ を使ったデザインパターンの改善と支援 >
Template Method

asato <asato@ncfreak.com>

最終更新日 : 2002/9/26 (2002/9/26 より)

Template Method パターン

1つのオペレーションにアルゴリズムのスケルトンを定義しておき、その中のいくつかのステップについては、サブクラスでの定義に任せることにする。Template Method パターンでは、アルゴリズムの構造を変えずに、アルゴリズム中のあるステップをサブクラスで再定義する。

AspectJ を使った Template Method に関するテクニックの種類

フックメソッドの導入

意図: サブクラスでオーバーライドできるようなフックメソッドを導入する。

問題: ある目的を満たすのに適切に見えるクラスが存在する。しかし、そのクラスのあるメソッドのアルゴリズム中のあるのステップの存在が目的を満たす邪魔をしている。もし、そのクラスが予めそのステップをサブクラスでオーバーライドできるような柔軟性のある設計を行っていたのであれば目的を満たすことができるのだが、そうはなっていない。

適用可能性:

結果: 実装:
  1. 3 通りの実装方法がある:
    1. 1 つのアスペクトのみを作る:
    2. AbstractClass を継承し、そのサブクラス内で内部アスペクトを定義する:
    3. AbstractClass のインスタンスを保持するようなクラスを作成するのと同時に、そのクラス内で内部アスペクトを定義する:

サンプルコード (実装方法 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);
		}
	}
}

妥協点: