次の 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 の最近の改善により、リフレクションは以前よりもはるかに高速になりました。