05 Proxyモード(二)

4935 ワード

回転:http://www.riabook.cn/doc/designpattern/
Design Pattern:Proxyモード(二)
 
Proxyモード(一)に続く議題として、代理の実現方法を見てみましょう.Static ProxyとDynamic Proxy.厳密にはモードの実現形態であるが、インスタンスによってProxyモードの適用がよりよくわかる.
まず、この例を見ると、記録動作であり、プログラムでは、後から見たり間違いを取り除いたりする時の情報として記録する必要があります.一番簡単な例は以下の通りです.ハローSpeaker.java.
import java.util.logging.*; 

public class HelloSpeaker { 
    private Logger logger = 
               Logger.getLogger(this.getClass().getName()); 

    public void hello(String name) { 
        logger.log(Level.INFO, "hello method starts....");

        System.out.println("Hello, " + name); 

        logger.log(Level.INFO, "hello method ends...."); 
    } 
} 
 
ハローSpeakerはハローメソッドを実行する時、この方法がすでに実行されて終了したことを記録したいです.最も簡単なやり方は上述のように実行の前後に記録動作を加えましたが、ロギングがハローSpeakerに介入しました.この記録はハローSpeakerではなく、ハローSpeakerに属しています.
もしプログラムの中でこのような記録の動作が至る所で必要とされるならば、上記のような書き方は必ず記録動作のプログラムコードをコピーしなければならないことをもたらして、記録動作を維持する困難度を増大させます.記録的な動作だけでなく、いくつかの非物件そのものの役割に関連する動きがある場合は、物件の中にも(例えば権限検査、事務管理など)が混入しており、荷物の負担がより重くなり、物件の職責が紛らわしいこともあります.
どうすればいいですか?以下の方法でいいかもしれません.まず中間面を定義してから、実際にこのインターフェースを作ります.
public interface IHello { 
    public void hello(String name); 
} 
 
ハローSpeaker.java
public class HelloSpeaker implements IHello { 
    public void hello(String name) { 
        System.out.println("Hello, " + name); 
    } 
} 
 
これからは代理品のHello Proxy:Hello Proxy.javaを作ります.
import java.util.logging.*; 

public class HelloProxy implements IHello { 
    private Logger logger = 
              Logger.getLogger(this.getClass().getName()); 
    private IHello helloObject; 

    public HelloProxy(IHello helloObject) { 
        this.helloObject = helloObject; 
    } 

    public void hello(String name) { 
        logger.log(Level.INFO, "hello method starts...."); 

        helloObject.hello(name); 

        logger.log(Level.INFO, "hello method ends...."); 
    } 
}
 
実行時はこのようにすることができます.IHello hello Proxy=new Hello Proxy(new Hello Speaker);ハローProxy.hello(「ジャスティン」)
本物のハローSpeakerを代理してハローワークを実行し、その前後に記録動作を加えてハローSpeakerが執筆中に記録動作に介入する必要がなく、ハローSpeakerがその役割に専念できるようにします.
これはStatic Proxyの基本的な例ですが、ご覧のように、代理品の一つのインターフェイスは一つのタイプのものにしかサービスしていません.そして、代理する方法が多いと、必ず各方法のために代理します.Static Proxyはプログラム規模が大きい時には必ずできません.
JavaはJDK 1.3の後でDynamic Proxy機能の開発に協力するカテゴリに参加します.特定のものと方法のために特定の代理を書く必要はありません.Dynamic Proxyを使って、ハンダが各物件にサービスを提供することができます.まず、Java.lang.reflect.InvocationHandler:LogHandler.java.java
import java.util.logging.*; 
import java.lang.reflect.*; 

public class LogHandler implements InvocationHandler { 
    private Logger logger = 
               Logger.getLogger(this.getClass().getName()); 
    private Object delegate; 

    public Object bind(Object delegate) { 
        this.delegate = delegate; 
        return Proxy.newProxyInstance(
                 delegate.getClass().getClassLoader(), 
                 delegate.getClass().getInterfaces(), 
                 this); 
    }
 
    public Object invoke(Object proxy, 
                         Method method, 
                         Object[] args) throws Throwable {
        Object result = null; 
        try { 
            logger.log(Level.INFO, 
                         "method starts..." + method); 
            result = method.invoke(delegate, args); 
            logger.log(Level.INFO, 
                         "method ends..." + method); 
        } catch (Exception e){ 
            logger.log(Level.INFO, e.toString()); 
        } 
        return result; 
    } 
} 
 
InvocationHandlerのinvoke()メソッドは、プロキシされたオブジェクトの方法名と実行パラメータが実際に実行する方法をmethod.invoke()に渡し、その前後に記録動作を加えて、method.invoke()に伝えられるものは、実際に実行された後のフィードバック結果である.
Dynamic Proxyは必ず中間面を宣言しなければならない.
public interface IHello { 
    public void hello(String name); 
} 
 
 ハローSpeaker.java
public class HelloSpeaker implements IHello { 
    public void hello(String name) { 
        System.out.println("Hello, " + name); 
    } 
} 
 
java.lang.reflect.ProxyのnewProxyInstance()は代理のもの、インターフェイスとハンドルから代理のものを生成します.私達は以下の方法でプログラムを実行できます.IHello hello Proxy=(IHello)logHandler.bind(new Hello Speaker);ハローProxy.hello(「ジャスティン」)
LogHandlerは特定のものとインターフェイスにサービスを提供していません.ハローSpeakerも記録に関する動作を挿入する必要はありません.記録動作の存在を意識する必要はありません.