Proxy

GamSa Ham·2022년 10월 24일
0

GoF디자인패턴

목록 보기
10/22
post-thumbnail

의도

다른 객체에 대한 접근을 제어하기 위한 대리자 또는 자리채움자 역할을 하는 객체를 둡니다.

다른이름

대리자(Surrogate)

동기

어떤 객체에 대한 접근을 제어하는 한 가지 이유는 실제로 그 객체를 사용할 수 있을 때까지 객체 생성과 초기화에 들어가는 비용 및 시간을 물지 않겠다는 것입니다.

문서 편집기를 예를 들어 봅시다. 레스터 이미지 그래픽 객체를 생성하는 비용이 많이 듭니다.

이미지 프록시라는 또 다른 객체를 사용하는 것입니다. 필요할 때만 이미지를 만들어 냅니다.

Text Document문서 편집기는 Grapghic 추상 클래스에 정의된 인터페이스를 통해서 이미지에 접근합니다.

ImageProxy는 요청에 따라 생성될 이미지를 위한 클래스로 디스크에 존재하는 이미지 파일 이름을 참조자로 관

리합니다. ImageProxy 생성자에 파일 이름이 매개변수로 전달됩니다.

이 참조자는 실제로 이미지를 인스턴스화하기 전까지는 의미가 없습니다. draw() 연산에 의해 실제로 그리기 전에

이 이미지를 인스턴스화 합니다.

getExtent() 연산은 인스턴화되었을 때만 이 요청을 실제 이미지에 전달하고, 그전에는 프록시가 이미지에 대한

크기 정보를 직접 제공합니다.

활용성

  1. 원격지 프록시(remote proxy)는 서로 다른 주소 공간에 존재하는 개체를 가리키는 대표 객체로, 로컬 환경에

    위치한다.

  2. 가상 프록시(virtual proxy) 요청이 있을 때만 고비용 객체를 생성합니다. 앞에서 예로 들었던 imageProxy

    에 해당합니다

  3. 보호용 프록시(protection proxy)는 원래 객채에 대한 실제 접근을 제어합니다. 이는 객체별로 접근 제어 권

    한이 다를 때 유용하게 사용 할 수 있습니다.

  4. 스마트 참조자(smart reference)는 원시 포인터의 대체용 객체로, 실제 객체에 접근이 일어날 때 추가적인

    행동을 수행합니다.

    • 실제 객채에 참조 횟수를 저장하다가 더는 참조가 없을때 해당 객체를 자동으로 없앱니다.
    • 맨 처음 영속적 저장소의 객체를 메모리로 옮깁니다.
    • 실제 객체에 접근하기 전에, 다른 객체가 그것을 변경하지 못하도록 실제 객체에 대해 잠금(lock)을 겁니다.

구조

참여자

  1. Proxy(ImageProxy)

    a. 실제로 참조할 대상에 대한 참조자를 관리합니다. RealSubject와 Subject인터페이스가 동일하면 프록

    시는 Subject에 대한 참조자를 갖습니다.

    b. Subject와 동일한 인터페이스를 제공하여 실제 대상을 대체할 수 있어야합니다.

    c. 실제 대상에 대한 접근을 제어하고 실제 대상의 생성과 삭제를 책임집니다.

    d. Proxy의 종류에 따라서 다음을 수행합니다.

    • 원격지 프록시는 요청 메시지와 인자를 인코딩하여 이를 다른 주소 공간에 있는 실제 대상에게 전달합니
    • 가상 프록시는 실제 대상에 대한 추가적 정보를 보유하여 실제 접근을 지연할 수 있도록 해야합니다.

    • 보호용 프록시는 요청 대상이 실제 요청할 수 있는 권한이 있는지 확인 합니다.

  1. Subject(Graphic) RealSubject와 Proxy에 공통적인 인터페이스를 정의하여, RealSubject가 요청되는

    곳에 Proxy를 사용할 수 있게 합니다.

  2. RealSubject(Image): 프록시가 대표하는 실제 객체입니다.

결과

프록시 패턴은 어떤 객체에 접근할 때 추가적인 간접화 통로를 제공합니다. 이렇게 추가된 간접화 통로는 프록시의

종류에 따라서 여러 가지 쓰임새가 있습니다.

  1. 원격지 프록시는 객체가 다른 주소 고안에 존재한다는 사실을 숨길 수 있습니다.

  2. 가상 프록시는 요구에 따라 객체를 생성하는 등 처리를 최적화 할 수 있습니다.

  3. 보호용 프록시 및 스마트 참조자는 객체가 접근할 때마다 추가 관리를 책임집니다. 객체를 생성할 것인지 삭제

    할 것인지 판단합니다.

예제 소스

package study.designpattern;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
class Point {
    private int x;
    private int y;
}

interface Event {}

interface Graphic {

    void draw() ;
    void handleMouse(Event event) ;
    Point getExtent();
    void load();
    void save() ;
}

class Image implements Graphic {

    @Override
    public void draw() {

    }

    @Override
    public void handleMouse(Event event) {

    }

    @Override
    public Point getExtent() {
        return null;
    }

    @Override
    public void load() {

    }

    @Override
    public void save() {

    }

    public Image(String fileName) {
    }

    // ...
}

class ImageProxy implements Graphic {

    public ImageProxy(String fileName) {
        this.fileName = fileName;
        this.extent = null; // 아직 이미지의 크기를 모릅니다.
        this.image = null;
    }

    @Override
    public void draw() {
        getImage().draw();
    }

    @Override
    public void handleMouse(Event event) {
        getImage().handleMouse(event);
    }

    @Override
    public Point getExtent() {

        if(extent == null) {
            extent = getImage().getExtent();
        }

        return extent;
    }

    @Override
    public void load() {
        System.out.println("ImageProxy.load");
    }

    @Override
    public void save() {
        System.out.println("ImageProxy.save");
    }

    protected Image getImage(){
        if(image == null) {
            image = new Image(fileName);
        }

        return image;
    }

    private Image image;
    private Point extent;
    private String fileName;

}

class TextDocument {

    public TextDocument() {
    }

    void insert(Graphic graphic) {

    }
}

public class ProxyPattern {

    public static void main(String[] args) {
        TextDocument textDocument = new TextDocument();

        textDocument.insert(new ImageProxy("anImageFileName"));
    }

}

관련 패턴

적응자(Adapter)는 자신이 개조할 객체가 정의된 인터페이스와 다른 인터페이스를 제공합니다.

이에 반해 프록시는 자신이 상대하는 대상과 동일한 인터페이스를 제공합니다.

프록시는 대상이 수행할 연산의 수행을 거부할 수 도 있기 때문에 처리 대상이 제공하는 인터페이스의 부분 집합일 수도 있습니다.

장식자(decorator)는 프록시와 구현 방법이 비슷한데 장식자는 그 사용 목적이 하나 이상의 서비스를 추가하기 위한 것이고 프록시는 객체에 대한 접근을 제어하는 목적이라는 점에서 차이가 있습니다

프록시는 장식자와 구현 방법에서도 많은 차이를 보입니다. 보호성 프록시는 장식자 구현 방법과 거의 유사하나,

원격 프록시는 실제 처리 대상을 직접 참조하도록 관리하지 않고 간접적 접근 방법을 관리합니다.

스프링에서 사용 예

  1. 스프링에서는 ProxyFactory 라는 기능으로 추상화 시키고 제공을 하고 있다.

인터페이스가 있는 경우에는 JDK 동적 프록시기능(InvocationHandler)을 활용하고 그렇지 않은 경우 CGLIB(methodInterceptor)를 활용을 한다.

  • JDK 동적 프록시 기능
  • CGLIB 동적 프록시 기능
  • Advice 개념 도입
  1. Spinrg Data JPA 항목에서는

@ManyToOne(fetch = LAZY) 항목으로 설정하게 되면

Member member;

MemberProxy 클래스가 만들어지게 되며

member.getName(); 항목을 호출시에 name 값이 없다고 판단되어 지면 실제 값을 불러온다.

profile
안녕하세요. 자바를 좋아하고 디자인 패턴, Refactoring, Clean Code에 관심이 많은 백엔드 개발자입니다.

0개의 댓글