最終更新日 : 2003/5/13 (2002/10/9 より)
Extension Object
考慮点
Component comp = ... Extension extension = comp.getExtension(SpecificExtension.class);もし、対象が要求された拡張をサポートしていなければ、クライアントには null を返す。ここで、クライアントは存在するクラスであればどんなクラスであっても指定可能であり、コンパイルが通ることに注意したい。つまり、以下のように String 型を引数として渡したとしても、もちろん、コンパイルは通る:
Component comp = ... Extension extension = comp.getExtension(String.class);問題は、これがプログラマのエラーであり、できるかぎりこのようなエラーを早期に発見したいということである。AspectJ をうまく使うことで、コンパイル時にこのようなありえない拡張を指定しているようなコードを検出できるだろうか? 次のようなアスペクトを考えるかもしれない:
public aspect ExtensionAspect {
declare error: invalidExtension(type) : "Invalid Extension";
pointcut invalidExtension(Class type) :
if ( isInvalidExtension(type) ) &&
args(type) &&
call( Extension Subject.getExtension(Class) );
private static boolean isInvalidExtension(Class type) {
return Extension.class.isAssignableFrom(type) == false;
}
}
しかし、これはうまくいかない。なぜ? 理由は、動的にマッチする特性をもっている Pointcut Designators (上記のコードでいえば args pointcut や if pointcut がこれに対応する) はこの局面では指定できないためである。したがって、このコードはコンパイルできない。
キャスト不要な拡張の獲得
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();
}
}
}
経緯:
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
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