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

最終更新日 : 2003/5/13 (2002/10/9 より)

Extension Object

オブジェクトのインタフェースの拡張に備える。Extension Object は、クラスへのインタフェースの追加を許し、クライアントは必要に応じたインタフェースを選んでアクセスできるようにする

考慮点

AspectJ を使って Extension Object パターンの欠点、あるいは、実装上において利点となりえるような点には以下のものが考えられる:

キャスト不要な拡張の獲得

package dp.exobj; // Client クラス以外すべて dp.exobj パッケージ

public interface Extension { }
public interface SpecificExtension extends Extension {
	public void specificInterface();
}
public interface Subject {
	public Extension getExtension(Class type);
}
public class ConcreteSubject implements Subject {
	public Extension getExtension(Class type) {

		if (type == SpecificExtension.class) {
			return new ConcreteSpecificExtension(this);
		} else {
			return null;
		}
	}
}
public class ConcreteSpecificExtension implements SpecificExtension {

	private ConcreteSubject subject;

	public ConcreteSpecificExtension(ConcreteSubject subject) {
		this.subject = subject;
	}

	public void specificInterface() {
		System.out.println("ConcreteSpecificExtension");
	}
}
public aspect ExtensionAspect {

	pointcut getInstance(Subject subject) : 
		args(subject) && call( * *.getInstance(Subject) );

	Object around(Subject subject) : getInstance(subject) {
		return subject.getExtension( thisJoinPointStaticPart.getSignature().getDeclaringType() );
	}

	public static SpecificExtension SpecificExtension.getInstance(Subject subject) {
		return null;
	}
}
package dp;

import dp.exobj.*;

public class Client {

	public static void main(String[] args) {

		Subject subject = new ConcreteSubject();

		SpecificExtension ext = SpecificExtension.getInstance( subject );

		if (ext != null) {
			ext.specificInterface();
		}
	}
}

経緯:
一見、この実装は、わざわざ特定のアスペクト (上記の例でいえば ExtensionAspect アスペクト) を定義することなく実現できそうな気がする。しかし、実際にはできない。interface は 実装を持てない ことを思い出そう。ExtensionAspect では、Introduction を使うことにより、この制約を克服し SpecificExtension interface に実装を定義できている。interface に実装を持たせるのは、通常の Java の仕様内においては不可能である。たとえば:
public interface SpecificExtension extends Extension {
	public void specificInterface();

	// コンパイル・エラー! interface に実装を定義することはできない
	public static SpecificExtension getInstance(Subject subject) { 
		return null;
	}
}
かといって、実装をなくせばいいのだろう、という考えも捨てざるえない。つまり、以下のコードはコンパイルが通らない:
public interface SpecificExtension extends Extension {
	public void specificInterface();

	// コンパイル・エラー! interface で static なメソッドを定義することはできない。
	public static SpecificExtension getInstance(Subject subject);
}
続いて考えるのは、SpecificExtension を interface でなく抽象クラスにしようという企みである。つまり:
public abstract class SpecificExtension implements Extension {
	public abstract void specificInterface();

	public static SpecificExtension getInstance(Subject subject) { 
		return null;
	}
}
この考えはうまくいく。だが、先ほどまで SpecificExtension interface を実装していればよかったはずの ConcreteSpecificExtension クラスが、今や SpecificExtension クラスを継承しなければいけなくなったという点にに注意する必要がある。これにより、やや柔軟性が欠ける設計になってしまったかもしれない。というのも、もしかすると ConcreteSpecificExtension クラスは、他のクラスを継承したかったかもしれないからである。Java では多重継承が許されていないため、これは問題になるかもしれない。

実装 - その 2

段階 - その 1

public interface Subject { }
public class ConcreteSubject implements Subject { }
public abstract aspect SpecificExtension {

	private static List subList = new Vector();

	protected SpecificExtension() {
		subList.add( this );
	}

	public static SpecificExtension extentionOf(Subject subject) {

		SpecificExtension ext = (SpecificExtension)subList.get(0);

		return ext.getExtention(subject);
	}

	protected abstract SpecificExtension getExtention(Subject subject);

	public abstract void specificInterface();

}
public aspect ConcreteSpecificExtension extends SpecificExtension perthis( subject() ) {

	private pointcut subject() : this(ConcreteSubject);

	public void specificInterface() {
		System.out.println("specificInterface");
	}

	protected SpecificExtension getExtention(Subject subject) {
		return ConcreteSpecificExtension.aspectOf(subject);
	}
}
public class Client {

	public static void main(String[] args) {

		Subject subject = new ConcreteSubject();

		SpecificExtension ext = SpecificExtension.extentionOf(subject);

		ext.specificInterface();
	}
}

段階 - その 2

public interface Subject { }
public class ConcreteSubjectA implements Subject { }
public class ConcreteSubjectB implements Subject { }
public class ConcreteSubjectC implements Subject { }
public abstract aspect SpecificExtension {

	public static SpecificExtension extentionOf(Subject subject) throws ExtentionNotFound {
		return findExtention(subject);
	}

	private static SpecificExtension findExtention(Subject subject) throws ExtentionNotFound {

		Iterator itr = ConcrereList.iterator();

		while( itr.hasNext() ) {

			Class ext = (Class)itr.next();

			try {
				Method method =
					ext.getMethod("aspectOf", new Class[] { Object.class } );

				Object[] param = new Object[] { subject };

				return (SpecificExtension)method.invoke(null, param);

			} catch (Exception e) {}
		}
	
		throw new ExtentionNotFound();
	}

	public abstract void specificInterface();

	private static aspect ConcrereList {

		private static List subList = new Vector();

		before() : staticinitialization(SpecificExtension+ && !SpecificExtension) {
			subList.add(thisJoinPoint.getSignature().getDeclaringType());
		}
		
		public static Iterator iterator() {
			return subList.iterator();
		}
	}
}
public aspect ConcreteSpecificExtensionA extends SpecificExtension perthis( subject() ) {

	private pointcut subject() : this(ConcreteSubjectA);

	public void specificInterface() {
		System.out.println("ConcreteSpecificExtensionA - specificInterface");
	}
}
public aspect ConcreteSpecificExtensionB extends SpecificExtension perthis( subject() ) {

	private pointcut subject() : this(ConcreteSubjectB);

	public void specificInterface() {
		System.out.println("ConcreteSpecificExtensionB - specificInterface");
	}
}
public class ExtentionNotFound extends Exception { }
public class Client {

	public static void main(String[] args) throws Exception {

		Subject subjectA = new ConcreteSubjectA();

		SpecificExtension ext = SpecificExtension.extentionOf(subjectA);

		ext.specificInterface(); // ConcreteSpecificExtensionA - specificInterface


		Subject subjectB = new ConcreteSubjectB();

		ext = SpecificExtension.extentionOf(subjectB);

		ext.specificInterface(); // ConcreteSpecificExtensionB - specificInterface


		Subject subjectC = new ConcreteSubjectC();

		ext = SpecificExtension.extentionOf(subjectC); // 例外

		ext.specificInterface(); 

	}
}

段階 - その 3

public abstract aspect Extension {
	
	private static Object findExtention(Object subject) throws ExtentionNotFound {

		for(Iterator itr = ConcrereList.iterator(); itr.hasNext(); ) {

			Class ext = (Class)itr.next();

			try {
				Method method =
					ext.getMethod("aspectOf", new Class[] { Object.class } );

				Object[] param = new Object[] { subject };

				return method.invoke(null, param);

			} catch (Exception e) {}
		}
	
		throw new ExtentionNotFound();
	}

	private static aspect Finder {

		Object around(Object subject) throws ExtentionNotFound :
			args(subject) && call( * Extension+.extentionOf(..) )
		{
			return Extension.findExtention(subject);
		}
	}

	private static aspect ConcrereList {

		private static List subList = new Vector();

		before() : staticinitialization(Extension+) {
			subList.add( thisJoinPoint.getSignature().getDeclaringType() );
		}
		
		public static Iterator iterator() {
			return subList.iterator();
		}
	}
}
public abstract aspect SpecificExtension extends Extension {

	public static SpecificExtension extentionOf(Subject subject) throws ExtentionNotFound {
		return null;
	}

	public abstract void specificInterface();
}

段階 - その 4

public abstract aspect ExtensionProtocol {

	public interface Extension { }

	// 変更なし
}
public abstract aspect SpecificExtension implements ExtensionProtocol.Extension {


	public static SpecificExtension extentionOf(Subject subject) throws ExtentionNotFound {
		return null;
	}

	public abstract void specificInterface();
}

段階 - その 5

subject オブジェクトを保持する。
public aspect ConcreteSpecificExtensionA extends SpecificExtension perthis( subject() ) {

	private ConcreteSubjectA subject;

	private pointcut subject() : this(ConcreteSubjectA);

	after(ConcreteSubjectA subject) :
		target(subject) && initialization( ConcreteSubjectA.new() )
	{
		this.subject = subject;
	}

	public void specificInterface() {
		System.out.println("ConcreteSpecificExtensionA - " + subject);
	}
}
public aspect ConcreteSpecificExtensionB extends SpecificExtension perthis( subject() ) {

	private ConcreteSubjectB subject;

	private pointcut subject() : this(ConcreteSubjectB);

	after(ConcreteSubjectB subject) :
		target(subject) && initialization( ConcreteSubjectB.new() )
	{
		this.subject = subject;
	}

	public void specificInterface() {
		System.out.println("ConcreteSpecificExtensionB - " + subject);
	}
}
public class Client {

	public static void main(String[] args) throws Exception {

		Subject subject1 = new ConcreteSubjectA();
		Subject subject2 = new ConcreteSubjectA();


		System.out.println("subject - " + subject1);
		System.out.println("subject - " + subject2);


		SpecificExtension ext = SpecificExtension.extentionOf(subject1);

		ext.specificInterface();


		ext = SpecificExtension.extentionOf(subject2);

		ext.specificInterface();
	}
}
subject - dp.extobj.ConcreteSubjectA@7c6768
subject - dp.extobj.ConcreteSubjectA@1690726
ConcreteSpecificExtensionA - dp.extobj.ConcreteSubjectA@7c6768
ConcreteSpecificExtensionA - dp.extobj.ConcreteSubjectA@1690726

参考文献

更新履歴

todo