Proxy Pattern
Overview
본 객체 대신 프록시 객체를 먼저 거쳐서 사용하는 방법
출처: Design pattern guru
Why to use?
- Lazy Loading 
생성시 많은 리소스가 필요할 때 유용 - 로깅
 - 캐싱 
 
When to use?
- 본 객체에 대한 접근 제어가 필요할 때
 - 로깅, 캐싱, 레이지 로딩 등 필요한 기능을 기존 코드 변경없이 
OCP 원칙을 지키며 추가하고 싶을 때 
How to use?
프록시 객체를 정의한다. 이 때, Interface 타입 필드를 갖는 property 를 생성자 주입받을 수 있게 간다. 
이는 구현체 참조 변수를 유지하기 위한 것으로 데코레이터 패턴과 유사하다.
GameService.java
public interface GameService {
    void loadGame() throws InterruptedException;
    void startGame();
}
DefaultGameService.java
public class DefaultGameService implements GameService{
    public void loadGame() throws InterruptedException {
        System.out.println("게임 로딩중입니다.");
        // 1초 지연
        Thread.sleep(1000L);
        System.out.println("로딩 완료");
    }
    public void startGame() {
        System.out.println("게임 시작!");
    }
}
GameServiceProxy.java
@Getter
@RequiredArgsConstructor(access = AccessLevel.PUBLIC)
public class GameServiceProxy implements GameService {
    private final GameService gameService;
    @Override
    public void loadGame() throws InterruptedException {
        // lazy Loading
//        if (gameService == null) {this.gameService = new DefaultGameService();}
        long before = System.currentTimeMillis();
        this.gameService.loadGame();
        long after = System.currentTimeMillis();
        System.out.println(after - before + "ms elapsed.");
    }
    @Override
    public void startGame() {
    }
}
GameServiceTest.java
class GameServiceTest {
    @Test
    void loadGameTest() throws InterruptedException {
        // Constructor DI
        GameServiceProxy gameServiceProxy = new GameServiceProxy(new DefaultGameService());
        gameServiceProxy.loadGame();
    }
}
실행결과
게임 로딩중입니다.
로딩 완료
1002ms elapsed.
📝 게임 서비스 구현체 코드를 전혀 건드리지 않고
(클라이언트 코드의 변경 없이 (main , 호출부))
변경에 닫혀있고, 확장에 열려있는 OCP 원칙을 지키며 메소드 실행 소요시간을 측정한다.
Class Diagram
Pros and Cons
장점
- 기존 코드(구현체, 클라이언트) 변경없이 프록시 객체 추가만으로 추가기능 구현 가능. 
ex) 지연로딩, 캐싱, 시간측정, 로깅 
단점
- 코드 복잡도 증가