반응형
의도
- 특정 객체에 대한 접근을 제어하거나 기능을 추가할 수 있는 패턴.
- 초기화 지연, 접근 제어, 로깅, 캐싱 등 다양하게 응용해 사용할 수 있다.
- 다른 객체에 대한 접근을 제어하기 위한 대리자 또는 자리채움자 역할을 하는 객체를 둔다.
장점
- 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있다.
- 기존 코드가 해야 하는 일만 유지할 수 있다.
- 기능 추가 및 초기화 지연 등으로 다양하게 활용할 수 있다.
단점
- 코드의 복잡도가 증가한다.
- 여러 다른 클래스에 동일한 기능(시간 측정)을 적용 시킨다면, 동일한 코드를 적용함에도 각각의 클래스에 해당하는 프록시 객체를 만들어서 적용해야 하기 때문에 코드의 중복이 많이 발생한다.
- 자바에서는 리플렉션에서 제공하는 다이나믹 프록시를 이용해서 해결할 수 있다.
활용성
- 원격지 프록시(remote proxy)
- 서로 다른 주소 공간에 존재하는 객체를 가리키는 대표 객체로 로컬 환경에 위치한다.
- 가상 프록시(virtual proxy)
- 요청이 있을 때만 필요한 고비용 객체를 생성한다.
- 보호용 프록시(protection proxy)
- 원래 객체에 대한 실제 접근을 제어한다.
- 객체별로 접근 제어 권한이 다를 때 유용하게 사용할 수 있다.
- 스마트 참조자(smart reference)
- 원시 포인터의 대체용 객체
- 실제 객체에 접근이 일어날 때 추가적인 행동을 수행한다.
결과
- 프록시 패턴은 어떤 객체에 접근할 때 추가적인 간접화 통로를 제공한다.
- 프록시 종류별 간접화 통로의 쓰임새
- 원격지 프록시는 객체가 다른 주소 공간에 존재한다는 사실을 숨길 수 있다.
- 가상 프록시는 요구에 따라 객체를 생성하는 등 처리를 최적화할 수 있다.
- 보호용 프록시 및 스마트 참조자는 객체가 접근할 때마다 추가 관리를 책임진다. 객체를 생성할 것인지 삭제할 것인지를 관리한다.
- 기록 시점 복사(copy-on-write)
- 요구가 들어올 때만 객체를 생성하는 개념과 관련있다.
- 복잡하고 큰 객체를 생성하는 것은 비용이 크다. 따라서 사본이 변경되지 않고 원본과 똑같다면 굳이 새롭게 생성하지 않아도 된다. 사본이 원본과 달라진 경우 원본을 이용해 새로운 사본을 만들어 내야 한다.
- 프록시를 사용해서 복사 절차를 미룸으로써 사본이 수정될 때만 실제 복사 비용을 물도록 할 수 있다.
- 기록 시점 복사는 중량급 객체에 대한 복사 비용을 현격하게 줄여준다.
협력 방법
- 프록시 클래스는 자신이 받은 요청을 RealSubject 객체에 전달한다.
구조

- 프로그램 실행 중 프록시 구조 객체 다이어그램

실제 구현 구조

소스코드
public class Client {
public static void main(String[] args) {
GameService gameService = new GameServiceImplProxy();
gameService.startGame();
}
}
//Subject
public interface GameService {
void startGame();
}
//RealSubject
public class GameServiceImpl implements GameService {
@Override
public void startGame() {
System.out.println("게임 시작");
}
}
//Proxy
public class GameServiceImplProxy implements GameService {
private GameService gameService;
@Override
public void startGame() {
long before = System.currentTimeMillis();
//lazy init
if (gameService == null) {
this.gameService = new GameServiceImpl();
}
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
//게임 로딩 과정
try {
for(int i = 0; i <= 100; i++) {
bufferedWriter.write(String.valueOf(i));
if(i % 10 == 0) {
bufferedWriter.write(System.lineSeparator());
}
}
bufferedWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
//게임 로딩 후 게임 시작
gameService.startGame(); // realSubject -> Request();
System.out.println(System.currentTimeMillis() - before);
}
}
관련 패턴
- 적응자는 자신이 개조할 객체가 정의된 인터페이스와 다른 인터페이스를 제공한다. 이에 반해, 프록시는 자신이 상대하는 대상과 동일한 인터페이스를 제공한다.
- 장식자는 프록시와 구현 방법이 비슷한데, 장식자는 그 사용 목적이 하나 이상의 서비스를 추가하기 위해서이고, 프록시는 객체에 대한 접근을 제어하는 목적이라는 점에서 차이가 있다.
- 구현 방법에서도 차이가 있다. 예를 들어, 보호성 프록시는 장식자 구현 방법과 거의 유사하나, 원격 프록시는 실제 처리 대상을 직접 참조하도록 관리하지 않고 간접적 접근 방법을 관리한다. 이 간접적 접근 방법은 호스트 식별자, 호스트 머신 내 주소 등을 포함한다. 가상 프록시는 파일 이름과 같은 간접적 참조자를 정의하지만, 궁극적으로는 직접적 참조자를 얻어온 후 이를 사용한다.
[참고자료]
리처드 헬름, 랄프 존슨, 존 블리시디스, 『GoF의 디자인 패턴 : 재사용성을 지닌 객체지향 소프트웨어의 핵심요소』, 김정아 번역, 프로텍미디어(2015)
http://www.cs.unc.edu/~stotts/GOF/hires/pat4efso.htm
반응형