Java 디자인 패턴-05.Proxy

(Java 디자인 패턴 스터디 모집 중 : https://github.com/bluedskim/javaDesignPatterns)

패턴 다이어그램

Proxy

“특정 클래스 대신 그 클래스와 동일한 인터페이스를 구현한 래퍼 wrapper 클래스 Proxy 를 이용한다” (출처:https://refactoring.guru/design-patterns/proxy)

해결하려는 문제

  1. 특정 객체에 대한 접근을 통제할 수 있는 일관된 방법이 필요할 때(보안, 권한 체크).
  2. 네트워크 접속, 메모리에 탑재된 대형 객체, 파일, 그 외 생성에 큰 비용이 발생하거나 복제가 불가능한 리소스들 등에 대한 캐시가 필요할 때.

특징/용도

  1. proxy클래스는 client의 요청을 subject에 단순히 전달하는 목적으로 사용하거나 subject의 메소드를 호출하기 이전, 이후에 특정 로직을 추가고자 할 때 사용한다.
  2. client입장에서는 proxy를 사용하든 subject를 사용하든 기능상 차이는 없다(black box).
  3. 지연된 초기화 Lazy initialization (virtual proxy) : 클래스 생성시 초기화 하는 것이 아니라 해당 객체의 메소드를 호출 할 때 초기화 하도록 함

고려사항

  1. 유사한 패턴과 비교
    • 어댑터 패턴은 subject와는 다른 인터페이스를 제공하는 반면 프록시는 동일한 인터페이스을 제공한다. 데코레이터 패턴은 subject보다 향상된(기능 추가) 인터페이스를 제공한다.
    • 파사드 패턴은 복잡한 엔티티들의 초기화에 대한 버퍼를 제공한다는 점에서 proxy패턴과 유사하다. 하지만 프록시는 동일한 인터페이스을 제공한다는 점에서 차이가 있다.
    • 데코레이터 패턴과 프록시 패턴은 복합 composition 을 사용한다는 점에서는 동일하지만 프록시 패턴은 subject 에 대한 라이프 사이클을 proxy내부에서 관리하는 반면 데코레이터 패턴에서는 클라이언트가 대상의 라이프사이클을 관리한다는 차이가 있다.
  2. Proxy내에서 클라이언트 목록을 관리할 수도 있다. 사용하는 클라이언트가 없다면 리소스를 해제하는 등의 처리작업 수행하고자 할 때 유용하다.

클래스 다이어그램

Proxy 패턴(김동석)

소스

  1. Proxy : Subject를 대체하고자 하는 객체(Proxy.java)
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    package net.dskim.desingpattern.proxy.solution;
    
    import lombok.extern.slf4j.Slf4j;
    import net.dskim.desingpattern.proxy.HeavyResource;
    import net.dskim.desingpattern.proxy.Resource;
    
    /**
     * Proxy객체도 역시 Resource 인터페이스를 구현해야 한다.
     */
    @Slf4j
    public class Proxy implements Resource {
    	/**
    	 * Proxy객체가 다루어야 할 Subject객체
    	 */
    	private HeavyResource heavyResource;
    
    	public Proxy() {
    		super();
    		log.info("Proxy 생성 완료");
    	}
    
    	/**
    	 * 처리 메소드를 호출 시
    	 * 지연된 초기화Lazy initialize 하도록 한다.
    	 */
    	@Override
    	public String process() {
    		// null check 하여 불필요한 초기화를 피한다.
    		if (heavyResource == null) {
    			this.heavyResource = new HeavyResource();
    		}
    		return heavyResource.process();
    	}
    }
  2. Subject : 숨기고자 하는 객체(HeavyResource.java)
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    package net.dskim.desingpattern.proxy;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * Subject 객체
     * 
     */
    @Slf4j
    public class HeavyResource implements Resource {
    	/**
    	 * 생성자 호출시 초기화에 많은 시간과 자원이 소모된다고 가정한다.
    	 */	
    	public HeavyResource() {
    		super();
    		log.info("HeavyResource 초기화 중...");
    		log.info("HeavyResource 초기화 완료");
    	}
    
    	/**
    	 * 실제 어떤 처리를 하는 메소드
    	 */
    	@Override
    	public String process() {
    		log.info("processing...");
    		return "processed";
    	}
    
    }
  3. Resource : proxy와 subject가 구현해야 하는 인터페이스(Resource.java)
    1
    2
    3
    4
    5
    6
    7
    8
    
    package net.dskim.desingpattern.proxy;
    
    /**
     * 모든 리소스가 구현해야 하는 인터페이스
     */
    public interface Resource {
        public String process();
    }

참고