구조 관련 : 프록시 패턴

    들어가기 전

    이 글은 백기선님의 GOF 디자인패턴 강의를 복습하며 작성한 글입니다. 

     


    프록시 패턴

    • 프록시 패턴 GOF : 특정 객체에 대한 접근을 제거하거나 기능을 추가할 수 있는 패턴
      • 초기화 지연, 접근 제어, 로깅, 캐싱 등에 사용 가능함.
    • Component
      • Subject, Proxy가 있음. 
      • Subject가 인터페이스 → implements로 프록시
      • Subject가 클래스 → 상속으로 프록시.
    • 특정한 객체의 오퍼레이션이 호출되면, 프록시 객체를 먼저 통과한 후에 오퍼레이션이 수행됨. 
    • 클라이언트는 프록시 객체를 통해서 원래 사용하려던 객체를 사용함. 

     


    기본 코드

    // 클라이언트 코드
    public class Client {
        public static void main(String[] args) {
            GameService gameService = new GameService();
            gameService.startGame();
        }
    }
    
    // GameService 코드.
    public class GameService {
        public void startGame() {
            System.out.println("이 자리에 오신 여러분을 진심으로 환영합니다.");
        }
    }

    현재 코드는 클라이언트가 게임을 시작하는 코드다. 만약 다음 기능을 추가해야한다면 어떻게 해야할까?

    1. 게임 시작 전/후로 걸린 시간을 측정해야 함. 
    2. 이 코드를 추가하기 위해 기존 코드를 수정할 수 없음. 

    이 때는 프록시 패턴을 적용해서 처리해 볼 수 있다. 


    작성 코드

    프록시 패턴에서 중요한 점은 기존 클라이언트는 기존 객체를 그대로 쓰는데 기능만 추가되어야 한다는 점이다. '다형성'을 활용해야 하는데 이를 위해서 상속이나 인터페이스를 사용할 수 있다.

    • 새로운 인터페이스 생성 허용 → 인터페이스로 프록시 객체 구현
    • 허용 X → 상속으로 처리 

    두 가지 방법으로 프록시 객체를 생성할 수 있고, 이를 통해 프록시 패턴을 적용해 볼 수 있다. 

    public interface GameService { void startGame(); }
    
    public class DefaultGameService implements GameService{
        @Override
        public void startGame() { System.out.println("이 자리에 오신 여러분을 진심으로 환영합니다."); }
    }
    
    public class GameServiceProxy implements GameService {
        private final GameService gameService;
        public GameServiceProxy(GameService gameService) { this.gameService = gameService; }
    
        @Override
        public void startGame() {
            // 관심사 추가.
            long start = System.currentTimeMillis();
            gameService.startGame();
            System.out.println("걸린 시간 = " + (System.currentTimeMillis() - start));
        }
    
    }

    위 코드에서는 인터페이스를 만들 수 있다는 가정 하에 프록시 객체를 생성한 코드다.

    • GameServiceProxy는 내부 참조로 기존 GameService를 가짐.
    • GameServiceProxy의 startGame()이 호출되면 시간을 측정하고, 기존 GameService의 startGame()을 호출함. 
    public class Client {
    
        public static void main(String[] args) throws InterruptedException {
            GameService target = new DefaultGameService();
            GameService gameService = new GameServiceProxy(target);
            gameService.startGame();
        }
    }

    클라이언트 입장에서는 GameService를 전달받고 gameService.startGame()을 호출한다. 따라서 클라이언트와 GameService의 기존 코드를 변경하지 않고 부가 기능을 추가했다. 

    클래스 다이어그램.

     


    Proxy 패턴의 장/단점

    • 장점 
      • 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있음.
      • 기존 코드가 해야하는 일만 유지할 수 있음.
      • 기능 추가 및 초기화 지연 등으로 다양하게 활용할 수 있음.
    • 단점
      • 코드의 복잡도가 증가함. 

    프록시 패턴은 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있다는 점이 유용하다. 그렇지만 인터페이스가 하나 추가될 수도 있으며 참조를 통해 dispatch 되므로 코드의 복잡도가 증가한다는 단점이 있다. 

    '디자인 패턴' 카테고리의 다른 글

    행동 관련 : State 패턴  (0) 2023.11.30
    행동 관련 : 템플릿 메서드 패턴  (0) 2023.11.30
    구조 관련 : 파사드 패턴  (0) 2023.11.25
    행동 관련 : 옵저버 패턴  (1) 2023.11.25
    행동 관련 : 메멘토 패턴  (1) 2023.11.25

    댓글

    Designed by JB FACTORY