-
프록시 패턴 (Proxy Pattern)디자인 패턴 2022. 7. 22. 22:51
프록시 패턴 (Proxy Pattern)
- 특정 객체에 대한 접근을 제어하거나 기능을 추가할 수 있는 패턴
- 특정 객체에 접근하기 전에 프록시 객체를 먼저 지난 후 접근하게 된다.
- 초기화 지연, 접근 제어, 로깅, 캐싱 등 다양하게 응용하여 사용할 수 있다.
프록시 패턴 적용 전
Client 가 startGame 이 실행되고 종료되기까지 얼마나 시간이 걸리는지 알기 위해서는 Client 코드의 main 시작 부분과 startGame 마지막 부분에 시간을 재면 된다.
public class Client { public static void main(String[] args) { GameService gameService = new GameService(); gameService.startGame(); } }
public class GameService { public void startGame() { System.out.println("게임을 시작합니다."); } }
프록시 패턴 적용 후
방법 1
GameService 를 전혀 손대지 않고 측정하는 방법을 살펴보자.
(여기에선 시간을 고의적으로 지연시키기 위해 sleep 을 사용한 것이다.)
public class Client { public static void main(String[] args) throws InterruptedException { GameService gameService = new GameServiceProxy(); //프록시 사용 gameService.startGame(); } }
public class GameService { public void startGame() throws InterruptedException { System.out.println("게임을 시작합니다."); Thread.sleep(1000L); } }
public class GameServiceProxy extends GameService { @Override public void startGame() throws InterruptedException { long before = System.currentTimeMillis(); super.startGame(); System.out.println(System.currentTimeMillis() - before); } }
Service 클래스를 상속받는 ServiceProxy(프록시 클래스) 를 만들어 startGame 메서드를 오버라이드 하였고, 이 곳에 시간 측정 코드를 추가하였다.
이 방법은 기존코드를 전혀 변경하지 않고 프록시 패턴을 적용하는 방법이였다.
방법2
이번에는 아래 프록시 패턴 클래스 다이어그램을 적용해보자.
Client 를 보면 프록시 안에 사용할 Service 객체를 넣어주고 있다.
public class Client { public static void main(String[] args) { // Client 가 DefaultGameService 를 쓰기 위해서는 프록시를 거쳐야함. GameService gameService = new GameServiceProxy(new DefaultGameService()); gameService.startGame(); } }
DefaultGameService 는 코드를 더 유연하게 하기 위해 GameService 인터페이스를 정의하고 구현하였다.
// Subject interface public interface GameService { void startGame(); }
// Real Subject interface public class DefaultGameService implements GameService { @Override public void startGame() { System.out.println("게임을 시작합니다!"); } }
프록시는 이전 데코레이터 패턴에서 본 것 처럼 GameService 를 가지고 있고, 이를 구현하고 있다.
public class GameServiceProxy implements GameService { private GameService gameService; public GameServiceProxy(GameService gameService) { this.gameService = gameService; } @Override public void startGame() { long before = System.currentTimeMillis(); gameService.startGame(); System.out.println(System.currentTimeMillis() - before); } }
프록시의 startGame 메서드를 보면 수행시간을 측정하는 로직과 Client 한테 받은 GameService 타입 객체의 startGame 메서드를 실행시키고 있다. 이렇게 하면 시간 측정외에도 다양한 로직을 추가할 수 있고, GameService 도 유연하게 변경할 수 있다.
또한 프록시 객체 내에서 GameService 를 결정하게 하여 지연 초기화(Lazy Initiallization)를 적용 할 수 있다.
아래 코드를 살펴보자.
public class GameServiceProxy implements GameService { private GameService gameService; @Override public void startGame() { long before = System.currentTimeMillis(); // Lazy Initiallization // 지연 로딩 외에도 특정 로직을 삽입하여 gameService 를 원하는 객체로 넣을 수 있다. if(this.gameService == null) this.gameService = new DefaultGameService(); gameService.startGame(); System.out.println(System.currentTimeMillis() - before); } }
public class Client { public static void main(String[] args) { // Client 가 DefaultGameService 를 쓰기 위해서는 프록시를 거쳐야함. GameService gameService = new GameServiceProxy(); gameService.startGame(); } }
장점과 단점
장점
- 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있다. (OCP)
- 기존 코드가 해야 하는 일만 유지할 수 있다. (SRP)
- 기능 추가 및 초기화 지연등으로 다양하게 활용할 수 있다.
단점
- 코드의 복잡도가 증가한다.
실무에선 어떻게 쓰이나?
자바
- 다이나믹 프록시
- java.lang.reflect.Proxy
스프링
- 스프링 AOP
참고
'디자인 패턴' 카테고리의 다른 글
커맨드 패턴 (Command Pattern) (0) 2022.08.02 책임 연쇄 패턴 (Chain of Responsibility Pattern) (0) 2022.08.02 플라이웨이트 패턴 (Flyweight Pattern) (0) 2022.07.22 퍼사드 패턴 (Facade Pattern) (0) 2022.07.19 데코레이터 패턴(Decorator Pattern) (0) 2022.07.19