Design Pattern | Proxy Pattern(プロキシパターン)

Proxyパターンとは?

  • Proxyという英単語は代理人代弁者という意味である。
  • 既存要素の代わりをする方式である。
  • オブジェクト指向では、「自分」も「代理人」もオブジェクトになる。
  • Proxyパターンは、忙しくて作業できないオブジェクト自身の代わりに、オブジェクトの代理人が一部の作業をこなす方式である。
  • Proxyパターンで重要なのは、フロー制御だけを行い、結果値を操作したり変更したりしてはいけないという点である。
  • あるオブジェクトにアクセスするために代理人を使用する。
  • GoFのデザインパターンでは、構造に関するデザインパターンに分類される。

Proxyパターンのサンプルプログラム

画面に文字を表示する「名前付きプリンター」のサンプルプログラムである。

Class Diagram
Proxy Pattern Class Diagram

1. Printableインターフェース

PrinterとPrinterProxyの共通インターフェースである。

Printable.java

package com.devkuma.designpattern.structural.proxy;

public interface Printable {
    // 名前設定
    void setPrinterName(String name);

    // 名前返却
    String getPrinterName();

    // 文字列表示(print out)
    void print(String string);
}

2. Printerクラス

名前付きプリンターを表すクラスである。(本人)

Printer.java

package com.devkuma.designpattern.structural.proxy;

public class Printer implements Printable {

    private String name;

    public Printer() {
        heavyJob("Printer의 인스턴스를 생성중");
    }

    public Printer(String name) {
        this.name = name;
        heavyJob("Printer의 인스턴스 (" + name + ")를 생성중");
    }

    public void setPrinterName(String name) {
        this.name = name;
    }

    public String getPrinterName() {
        return name;
    }

    public void print(String string) {
        System.out.println("=== " + name + " ===");
        System.out.println(string);
    }

    private void heavyJob(String msg) {
        System.out.print(msg);
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            System.out.print(".");
        }
        System.out.println("완료");
    }
}

3. PrinterProxyクラス

名前付きプリンターを表すクラスである。(代理人)

PrinterProxy.java

package com.devkuma.designpattern.structural.proxy;

public class PrinterProxy implements Printable {

    private String name;
    private Printer real;

    public PrinterProxy() {
    }

    public PrinterProxy(String name) {
        this.name = name;
    }

    public synchronized void setPrinterName(String name) {
        if (real != null) {
            real.setPrinterName(name);
        }
        this.name = name;
    }

    public String getPrinterName() {
        return name;
    }

    public void print(String string) {
        realize();
        real.print(string);
    }

    private synchronized void realize() {
        if (real == null) {
            real = new Printer(name);
        }
    }
}

4. Mainクラス

メイン処理を実行するクラスである。

Main.java

package com.devkuma.designpattern.structural.proxy;

public class Main {
    public static void main(String[] args) {
        Printable p = new PrinterProxy("Alice");
        System.out.println("현재 이름은 " + p.getPrinterName() + " 입니다.");
        p.setPrinterName("Bob");
        System.out.println("현재 이름은 " + p.getPrinterName() + " 입니다.");
        p.print("Hello, world.");
    }
}

5. 実行結果

현재 이름은 Alice 입니다.
현재 이름은 Bob 입니다.
Printer의 인스턴스 (Bob)를 생성중.....완료
=== Bob ===
Hello, world.

Proxyパターンのメリット

Proxyパターンでは、Proxyが代理人となり、可能な限り処理を肩代わりする。
サンプルプログラムでは、Proxy役を使うことで、実際にprintするまで重い処理(インスタンス生成)を遅延できた。
例えば、初期化に時間がかかる機能が多数存在するシステムの場合、起動時点で利用しない機能まで全部初期化してしまうと、アプリケーションの起動に時間がかかってしまう。
PrinterProxyクラスとPrinterクラスの2つに分けず、Printerクラス内に最初から遅延機能を入れておくこともできるが、クラスを分けることでプログラムの部品化が進み、個別に機能を追加しやすくなる。