Lee's Grow up

[디자인패턴/Design Pattern] Proxy Pattern / 프록시 패턴 본문

PROGRAMMING/디자인패턴

[디자인패턴/Design Pattern] Proxy Pattern / 프록시 패턴

효기로그 2019. 12. 19. 14:37
반응형

관련 내용은 [자바 언어로 배우는 디자인 패턴 입문],[Head First Design Pattern]의 내용을 참고해서 정리한 내용입니다.
잘못된 부분은 댓글로 피드백 부탁드립니다.

1. Proxy 패턴이란?


어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 패턴

2. Proxy 패턴의 등장인물


  • Subject(주체)의 역할
    1. Proxy 역할과 RealSubject 역할을 동일시하기 위한 인터페이스(API)를 결정합니다. 이 덕분에 클라이언트는 둘의 역할 차이를 몰라도 됩니다.
  • Proxy(대리인)의 역할
    1. Client의 요구를 할 수 있을 만큼 처리하고, 필요할 경우 RealSubject에게 처리를 맡깁니다.
  • RealSubject(실제의 주체)의 역할
    1. Proxy에서 요청이 들어왔을때 요청에 대한 응답을 합니다.

3. 예제


3-1. 예제 클래스 다이어그램

3-2. PritableSubject 인터페이스
public interface PrintableSubject {
    void setPrinterName(String name);
    String getPrinterName();
    void print(String string);
}

역할 설명에서 언급했던것처럼 ProxyRealSubject역할의 인터페이스(API)를 정의해줍니다.

3-3. PrinterRealSubject 클래스
public class PrinterRealSubject implements PrintableSubject {
    private String name;

    public PrinterRealSubject(String name) {
        this.name = name;
        heavyJob("PrinterRealSubject[" + name + "] 인스턴스 생성중..");
    }

    private void heavyJob(String string) {
        // TODO Auto-generated method stub
        System.out.println(string);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("생성 완료...");
    }

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

    public String getPrinterName() {
        return name;
    }

    public void print(String string) {
        System.out.println(string);
    }
}

인터페이스를 구현하기 위해 setPrinterName(), getPrinterName(), print() 를 재정의해주고, 객체의 인스턴스 생성의 비용을 무겁게 하기 위해 heavyJob() 이란 메소드를 통해 3초를 지연시킵니다.

3-4. PrinterProxy 클래스
public class PrinterProxy implements PrintableSubject {

    private String name;
    private PrinterRealSubject real;

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

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

    }

    @Override
    public String getPrinterName() {
        return name;
    }

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

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

}

대리인으로써 처리가 가능한 부분은 객체가 맡아서 처리를 해줍니다. ex ) getPrinterName()
그리고 실제 RealJubject가 필요한 시점에 해당 객체에게 요청을 위임합니다.

3-5. Main 클래스

public class Main {
    public static void main(String[] args) {
        PrintableSubject p = new PrinterProxy("Simple");

        // 프록시가 실행 됨
        System.out.println(p.getPrinterName());

        // realSubject가 실행 
        p.print("프린트 요청");
    }
}
실행 결과
Simple
PrinterRealSubject[Simple] 인스턴스 생성중..
생성 완료...
프린트 요청
왜 사용할까 ?

시작하기에 앞서 Proxy Pattern의 정의를 아래와 같이 설명했습니다.

  • 어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 패턴

그런데 예제만 보고는 왜? 라는 생각이 드실 수 있습니다.

  • 로직에 많은 비용이 필요하면 처음부터 Printer 클래스에서 분리해서 사용하면 되는거 아니야?

라는 의문에는 디자인의 원칙에 의해서 객체들의 역할을 분리해서 개별적으로 수정이 가능하게 하기 위함입니다. 즉 유지보수가 용이하고 클라이언트 입장에서는 지연이 필요할경우 Proxy객체를 사용 필요없이 사용할 경우 PrinterRealSubject를 사용할 수 있습니다.

마지막으로 Proxy Pattern의 정의를 비용이 많이 필요한 경우 역할을 나눠서 지연시킬 수 있는 패턴이라고 정의하지 않고, 본문처럼 포괄적으로 설명한 이유는 무엇일까? Proxy Pattern은 여러 종류 존재하기 때문에 포괄적인 의미만 전달했습니다.

Proxy Pattern의 종류
  • Virtual Proxy(가상 프록시) : 예제에 해당하는 프록시이며, 인스턴스가 필요한 시점에 생성, 초기화를 실행합니다.
  • Remote Proxy(원격 프록시 ) : 네트워크가 연결된 상황에서 원격으로 로컬 객체를 원격 객체처럼 사용할 수 있게 해줍니다. Java RMI 등을 사용
  • Access Proxy(보호 프록시) : RealSubject 기능에 대해서 엑세스 제한을 설정합니다.
  • Caching Proxy(캐싱 프록시) : 지연이 많이 드는 작업을 임시로 저장, 여러 클라이언트에게 공유합니다.
  • 기타 : 방화벽 프록시, 스마트 레퍼런스 프록시, 동기화 프록시, 복잡도 숨김 프록시, 지연 복사 프록시 등.

4. 관련 패턴


  • Adapter 패턴 : 연결 해준다는 관점은 같지만 Adapter는 서로 다른 인터페이스를 연결해줍니다. link
  • Decorator 패턴 : 구현은 비슷하지만 목적이 다릅니다. 새로운 기능을 추가할 때 사용합니다. link

5. 비교하기


객체를 감싸는 패턴이 많은데 구분을 하기 위해 차이점을 정리했습니다,

  • Decorator 패턴 : 다른 객체를 감싸서 새로운 행동을 추가해줍니다. 기능 확장 link
  • Facade 패턴 : 여러 객체를 감싸서 인터페이스를 단순화시킵니다. 기능 단순화 link
  • Adapter 패턴 : 다른 객체를 감싸서 다른 인터페이스를 제공합니다. 호환성 link
  • Proxy 패턴 : 다른 객체를 감싸서 접근을 제어합니다. 접근제어 link
반응형
Comments