動的切り替えの動的エージェント
6427 ワード
名前はちょっと回りくどいですが、目的は簡単に明確です。動的な代理を実現したいという対象の実例は、運行時にも切り替わります。前提条件とプログラムの文脈を先に理解してください。例えば、次のようなインターフェースがあります。
この条件は、インターフェースの異なるインスタンスがオブジェクトに入る必要があるが、このオブジェクトが持っているインスタンスは変更できず、オブジェクトも再作成できないことである。
こんなに多いというのは代理モードを使うということではないですか?はい、代理モードはまさにこのような問題を解決することができます。
そこで簡単な代理ができました。
ダイナミックエージェントは何ですか?変化に適応するために、何か変化がありますか?インターフェースの変化もしインターフェースのResponderが一つの方法を追加したら、ReponderWrapperはもう一つのインターフェースを追加します。Responder 1つの方法のパラメータを変更すると、ReponderWrapperはインターフェースの実例の新しい方法を修正して呼び出します。このように類推しても大丈夫です。しかし、インターフェースの方法が多くなりますと、インターフェースの実現の種類が多くなります。ダイナミックエージェントはこのような複雑な作業を解決することができます。ダイナミックエージェントとしてResponderWrapperを書くのはとても簡単です。
public interface Responder {
void onMethod1(String s);
int onMethod2();
void onMethod3();
}
インターフェースの一例Responder r1
を一部のクラスp1 = new Presenter(r1)
(または外部SDK)に導入し、実行時には異なるResponderのインスタンスr 2を生成し、r 2がr 1を代替できることを望むが、Presenterクラスについては感知できないので、関心を持たない。明らかに、私たちのプログラムコンテキストはResponderのインスタンスに対する制御(作成/転送)を実現することができる。しかし、現在の問題はPresenter類の構造パラメータのみがResponderに導入されたもので、setResponder(Responder r)
という方法はありません。プログラムの文脈が許すなら、このようなこともないです。この条件は、インターフェースの異なるインスタンスがオブジェクトに入る必要があるが、このオブジェクトが持っているインスタンスは変更できず、オブジェクトも再作成できないことである。
こんなに多いというのは代理モードを使うということではないですか?はい、代理モードはまさにこのような問題を解決することができます。
そこで簡単な代理ができました。
public class ResponderWrapper implements Responder {
private final Responder impl;
public ResponderWrapper(Responder r) {
impl = r;
}
@Override
void onMethod1(String s) {
impl.onMethod1(s);
}
@Override
int onMethod2() {
return impl.onMethod2();
}
@Override
void onMethod3() {
impl.onMethod3();
}
}
プロキシオブジェクトを動的に変更するためにset方法を追加します。 void setResponder(Responder r) {
impl = r;
}
Presenterオブジェクトに導入された例は、r 1ではなく、r 1である。wrapper = new ResponderWrapper(r1);
p1 = new Presenter(wrapper);
この時に新しいResponderの実例r 2を創立して、私達はただ必要とします。wrapper.setResponder(r2);
目的を達成することができます。p 1かp 1か、p 1が持っている例かそれとも同じ例か、切り替え前のp 1調はr 1の実現であり、切り替え後は自然にr 2の実装が呼び出されます。このようなエージェントは非常に一般的な静的エージェントです。機能の実現だけでは十分OKです。問題はありません。動的エージェントを使用する必要がありますか?わけではないダイナミックエージェントは何ですか?変化に適応するために、何か変化がありますか?インターフェースの変化もしインターフェースのResponderが一つの方法を追加したら、ReponderWrapperはもう一つのインターフェースを追加します。Responder 1つの方法のパラメータを変更すると、ReponderWrapperはインターフェースの実例の新しい方法を修正して呼び出します。このように類推しても大丈夫です。しかし、インターフェースの方法が多くなりますと、インターフェースの実現の種類が多くなります。ダイナミックエージェントはこのような複雑な作業を解決することができます。ダイナミックエージェントとしてResponderWrapperを書くのはとても簡単です。
public final class ResponderWrapper {
public static Responder wrap(final Responder responder) {
return (Responder) Proxy.newProxyInstance(Responder.class.getClassLoader(),
Responder.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(responder, args);
}
});
}
}
しかし、このような書き込みは動的切替の必要性を満たすことができないので、最終的な目的はこれである。動的エージェント形式で作成されたエージェントインスタンスは、保有するオブジェクトのインスタンスを動的に切り換えることができるが、setResponder
がr 1に入ってくると、匿名のオブジェクトが持つResponderオブジェクトはr 1でしかないので、ResponderWrapper.wrap
がここのmethod.invoke(responder, args)
を動的に切り換えることができるようにしたい。能力は普通インターフェースの形で実現されます。public final class ResponderWrapper {
public interface Provider {
Responder get();
}
public static Responder wrap(final Provider provider) {
return (Responder) Proxy.newProxyInstance(Responder.class.getClassLoader(),
Responder.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(provider.get(), args);
}
});
}
プログラムコンテキストresponder
インターフェースが実装され、インターフェース方法が起動されたときに戻ってくる例は、現在のResponderであり、いつ切り替わるかに関係なく、現在のResponderである。mResonder = r1;
wrapper = ResponderWrapper.wrap(new ResponderWrapper.Provider() {
@Override
public ResponderWrapper.Responder get() {
return mResponder;
}
});
p1 = new Presenter(wrapper);
...
mResonder = r2;
インターフェースが重すぎると、実際にはこのような形でもインタフェースを使わないで実現できます。私達が最終的に必要なのはResponderの実例です。インターフェースメソッドが呼び出された時にこの実例の対応方法を呼び出すことができるだけです。public final class ResponderWrapper {
public static final class Holder {
public Responder responder;
}
public static Responder wrap(final Holder holder) {
return (Responder) Proxy.newProxyInstance(Responder.class.getClassLoader(),
Responder.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(holder.responder, args);
}
});
}
}
プログラムコンテキストは、ReponderWrapper.Holderの例を持ち、必要な時には別のResorderの例を設定します。mHolder = new ResponderWrapper.Holder(r1);
wrapper = ResponderWrapper.wrap(holder)
p1 = new Presenter(wrapper);
...
mHolder.responder = r2
范型の抽象的なあらゆるインターフェース類を使えば、もっと一般的に書くことができます。public final class ResponderWrapper {
public static final class Holder {
public T responder;
}
@SuppressWarnings("unchecked")
public static T wrap(final Holder holder) {
T r = holder.responder;
return (T) Proxy.newProxyInstance(r.getClass().getClassLoader(),
r.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(holder.responder, args);
}
});
}
}
ここでResponderWrapper.Provider
を一時的に利用してholder.responder
rおよびClassLoade
を取得しても、クラスオブジェクトを完全に入力することができる。public final class ResponderWrapper {
public static final class Holder {
public T responder;
}
@SuppressWarnings("unchecked")
public static T wrap(final Holder holder, final Class clazz) {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(),
clazz.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(holder.responder, args);
}
});
}
}
これは私達のいわゆる動的切り替えの動的エージェントです。