Design Pattern | Proxy Flyweight (프록시 패턴)


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 클래스 안에 처음부터 지연 기능을 넣어 둘 수도 있지만, 클래스를 나누는 것으로 프로그램의 부품화가 진행되어 개별적으로 기능을 더 할 수 있는 거다.