次の 2 つの方法があります。
- アダプター パターンの使用
- 使用する
Proxy
アダプター アプローチは単純ですが柔軟性が低く、Proxy
アプローチはより複雑ですが柔軟性が高くなります。アプローチはより複雑ですProxy
が、その複雑さはすべていくつかのクラスに制限されています。
アダプタ
アダプターのパターンは単純です。あなたの例では、次のように1つのクラスになります。
public class QuietFooAdapter implements Foo {
private QuietFoo quietFoo;
public QuietFooAdapter(QuietFoo quietFoo) {
this.quietFoo = quietFoo;
}
public void bar() {
quietFoo.bar();
}
}
それを使用するには:
Foo foo = new QuietFooAdapter(new QuietFoo());
foo.bar();
これは良いことですが、アダプターを作成するクラスが複数ある場合は、ラップする必要があるクラスごとに新しいアダプターが必要になるため、面倒な場合があります。
JavaのProxy
クラス
Proxy
は、リフレクション ライブラリの一部であるネイティブ Java クラスであり、より一般的なリフレクティブ ソリューションを作成できます。これには 3 つの部分が含まれます。
- インターフェイス (この場合は
Foo
)
- の
InvocationHandler
- プロキシの作成 (
Proxy.newProxyInstance
)
インターフェースはすでにあるので、問題ありません。
これInvocationHandler
は、リフレクションを介して「自動適応」を行う場所です。
public class AdapterInvocationHandler implements InvocationHandler {
private Object target;
private Class<?> targetClass;
public AdapterInvocationHandler(Object target) {
this.target = target;
targetClass = target.getClass();
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Method targetMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());
if (!method.getReturnType().isAssignableFrom(targetMethod.getReturnType()))
throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not support: " + method.toGenericString());
return targetMethod.invoke(target, args);
} catch (NoSuchMethodException ex) {
throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not support: " + method.toGenericString());
} catch (IllegalAccessException ex) {
throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not declare method to be public: " + method.toGenericString());
} catch (InvocationTargetException ex) {
// May throw a NullPointerException if there is no target exception
throw ex.getTargetException();
}
}
}
ここで重要なコードはtry
ブロックにあります。これにより、プロキシで呼び出されたメソッド呼び出しを内部target
オブジェクトに適合させるプロセスが処理されます。サポートされていないインターフェイスでメソッドが呼び出された場合 (非public
、間違った戻り値の型、またはフラット アウトが存在しない)、 をスローしUnsupportedOperationException
ます。をキャッチするInvocationTargetException
と、それを引き起こした例外を 経由で再スローしますInvocationTargetException.getTargetException
。これは、反射的に呼び出したメソッドが例外をスローしたときに発生します。Java はそれを新しい例外でラップし、その新しい例外をスローします。
次に、アダプターを作成するために何かが必要です。
public class AdapterFactory {
public static <T> T createAdapter(Object target, Class<T> interfaceClass) {
if (!interfaceClass.isInterface())
throw new IllegalArgumentException("Must be an interface: " + interfaceClass.getName());
return (T) Proxy.newProxyInstance(null, new Class<?>[] { interfaceClass }, new AdapterInvocationHandler(target));
}
}
AdapterInvocationHandler
必要に応じて、クラスをクラスにネストしてAdapterFactory
、すべてが自己完結型になるようにすることもできますAdapterFactory
。
それを使用するには:
Foo foo = AdapterFactory.createAdapter(new QuietFoo(), Foo.class);
foo.bar();
QuietFoo
このアプローチは、単一のアダプターを実装するよりも多くのコードを必要としますが、 andの例だけでなく、任意のクラスとインターフェイスのペアの自動アダプターを作成するために使用できるほど十分に汎用的Foo
です。確かに、このメソッドはリフレクションを使用します (Proxy
クラスはリフレクションを使用し、私たちの と同様InvocationHandler
)、これは遅くなる可能性がありますが、JVM の最近の改善により、リフレクションは以前よりもはるかに高速になりました。