Proxyパターン

Proxyパターン

Proxyは代理、委任という意味なので、 Proxyパターンとは、オブジェクトへのアクセスする際に代理のオブジェクトを使うようなデザインパターンです。Proxyという用語はインターネットで使われているProxyサーバを連想させます。NetscapeやIEなどのWebブラウザから直接Webサーバへアクセスするのではなく、代理である Proxyサーバを通して行います。Proxyサーバの目的は、ネットワークのセキュリティ機能やキャッシュによるネットワーク負荷を下げるためです。

ProxyパターンのProxyの種類には、以下の3つがあります。

  • remote proxy
  • virtual proxy
  • protection proxy

remote proxyは、分散オブジェクトのようなネットワーク上の別のサーバのオブジェクトにアクセスする場合に、IPアドレスやプロトコルなどのネットワークに関連した情報を隠して、プログラムからあたかもローカルなオブジェクトのように扱うことようなパターンです。
virtual proxyは、オブジェクトを生成するときに、オブジェクトのサイズが大きいなどの理由により、実体をロードしたくない時に、仮にProxyとして実体なしでオブジェクトを作成しておきます。
例えば、画像イメージを扱うWebブラウザのようなプログラムで、はじめにイメージを表示させる必要がないとします。この場合は、ページ内のイメージオブジェクトをProxyとして生成させるだけでイメージを表示させるモードに替わったり、イメージがクリックされた時に、実体をロードして表示します。これにより、ページの初期表示のスピードの向上やプログラムのメモリ消費の節約になります。
protection proxyは、オブジェクトへアクセスするセキュリティの問題を解決します。アクセスしたいオブジェクトが直接すべてのオブジェクトからアクセスされたくない場合に、Proxyとしてアクセス用のオブジェクトを作り、このオブジェクトだけがアクセスをできるようにします。
Apple社(旧Next社) OpenStep環境のInterfaceBuilderのdelegate機能などがその例です。Proxyによるアクセス制御は、クライアント/サーバ・システムなどの分散環境では、最も重要な要素です。それでは、例をあげてProxyパターンを見ていきます。ここでは、図3のようにワープロソフトで画像イメージを取り扱うアプリケーションを考えてみます。

virtual proxyの例で説明したWebブラウザと同様に、ワープロのアプリケーションが文書を開いたときには画像は表示せず、画像モードをONにしたり、画像をクリックしたときに表示を行います。文書中に画像があった場合はイメージProxyオブジェクトだけを生成して実体であるイメージオブジェクトは生成しません。画像を描画するときにはじめて、実体のイメージオブジェクトを生成してファイルをロードする仕組みです。 それでは図4のクラス図をみてみましょう。

このプラグラムではJavaのGUIフレームワークAWTをつかっているので、イメージクラスのスーパークラスはAWT のPanelクラスになります。Panelクラスのpaint()をImageDisplay、ImageDisplayProxyクラスではオーバーライトしています。
JavaのAppletプログラミングでは、init()、start()、stop()、run()、paint()、update ()などのメソッドの使い方が決められているので、それにあわせたコードを書かなければいけません。またJavaでのプログラミングは、Java言語を習得するというよりも、これらのメソッドで構成されているフレームワークを使いこなさなければいけません。
Java言語は簡単で理解しやすいと言われているわりに、Javaプログラマの数が少ないのは、このハードルを越すのが難しいからではないでしょうか。この連載は、Javaプログラミング講座ではないので、話をもとに戻します。さらに、Javaのコードも見ながら、説明していきます。

                                ProxyサンプルのJavaコード
    
class ImageDisplayProxy extends Panel {

    ImageDisplay imgp;
    Applet apl;
    String imagefile;

    ImageDisplayProxy(Applet applet, String filename) {
        apl = applet;
        imagefile = filename;
        }

    public void update(Graphics g) {
        paint(g);
    }

    ImageDisplay getImage(){
        if (imgp == null){
            imgp = new ImageDisplay(apl,imagefile);
            }
        return imgp;
        }

    public void paint(Graphics g) {
        getImage().paint(g,this);
        }
    }

    class ImageDisplay extends Panel {

    Image img;
    ImageDisplay(Applet applet, String filename) {
        img = applet.getImage(applet.getCodeBase(),filename);
        }

    public void paint(Graphics g,Panel ip) {
        g.drawImage(img, 0, 0, ip);
        super.paint(g);
        }
    }
                    
            Proxyサンプルのメイン部分
                    
    public class ProxyPattern extends Applet {
        public void init() {
            setLayout(new BorderLayout());
            ImageDisplayProxy idp = new ImageDisplayProxy(this,"images/yellow_box.jpg");
            add("Center", idp);
        }
    }
                    

メイン部分のinit()内で、new ImageDisplayProxy()としてイメージProxyオブジェクトidpを生成しています。ImageDisplayProxyクラスのコンストラクタImageDisplayProxy()では、Applet名と画像ファイル名を保存しているだけで、イメージの実体をロードしていません。さらに、メイン部分のAppletから更新要求がくるとImageDisplayProxyクラスのupdate()が呼び出されます。
update()の中で、paint()を呼びだし、さらにその中ではgetImage().paint()としています。ここがポイントで、getImage()はimgpがまだ生成されていなければ、new ImageDisplay()で実体をロードしてきます。imgp == nullという条件があるので、二重にロードすることは起こりません。そして、getImage()のリターンであるImageDisplayオブジェクトのpaint()メソッドを呼び出しています。このpaint()内で、drawImage()、paint()によりApplet上で描画が行われるわけです。
この例はvirtual proxyでしたが、Proxyパターンは、CORBA、ActiveX、RMIなどの分散オブジェクトでアプリケーションを開発する時にはとても有効で、米国では盛んに使われています。