헤드 퍼스트 디자인 패턴 | 에릭 프리먼 | 한빛미디어- 교보ebook

14가지 GoF 필살 패턴!, 경력과 세대를 넘어 오랫동안 객체지향 개발자의 성장을 도와준 디자인 패턴 교과서의 화려한 귀환! 》 2005년부터 디자인 패턴 도서 분야 부동의 1위 》 디자인 패턴의 고

ebook-product.kyobobook.co.kr

프록시는 접근을 제어하고 관리한다.

다양한 변형 프록시가 존재한다.

프록시 패턴의 정의

특정 객체로의 접근을 제어하는 대리인을 제공한다.

  • 원격 프록시를 써서 원격 객체로 접근 제어
  • 가상 프록시로 생성하기 힘든 자원으로 접근 제어
  • 보호 프록시로 접근 권한이 필요한 자원으로 접근 제어

 

자바는 java.lang.reflect 패키지를 제공한다. 이 패키지 기능을 사용하여 프록시 기능을 구현할 수 있다.

이렇게 만들어진 프록시는 런타임 중 생성되서, 동적 프록시(dynamic proxy)라 한다.

 

리플랙션 패키지를 사용하면 자바에서 Proxy 클래스를 생성해 주므로 필요한 정보만 전달해주면 된다.

InvocationHandler 가 핵심이다.

 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;

public class ProtectProxyTest {
	static interface Person{
		String getName();
		String getGender();
		String getInterests();
		int getGeekRating();
		
		void setName(String name);
		void setGender(String gender);
		void setInterests(String interests);
		void setGeekRating(int rating);
	}
	
	static class PersonImpl implements Person{
		String name;
		String gender;
		String interests;
		int rating;
		int ratingCount = 0;
		
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public String getGender() {
			return gender;
		}
		@Override
		public int getGeekRating() {
			if(ratingCount == 0 ) return 0;
			return (rating/ratingCount);
		}
		public void setGender(String gender) {
			this.gender = gender;
		}
		public String getInterests() {
			return interests;
		}
		public void setInterests(String interests) {
			this.interests = interests;
		}
		
		@Override
		public void setGeekRating(int rating) {
			this.rating = rating;
			ratingCount++;
		}
		
	}
	static class OwnerInvocationHandler implements InvocationHandler{
		Person person;
		
		public OwnerInvocationHandler(Person person) {
			this.person = person;
		}
		@Override
		//proxy객체참조, method 객체가 호출한 메서드 정보, args 메서드 인자정보
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			try {
				if(method.getName().startsWith("get")) {
					return method.invoke(person, args);
				}else if(method.getName().startsWith("setGeekRating")) {
					//나한테 평가는 불가
					throw new IllegalAccessException();
				}else if(method.getName().startsWith("set")) {
					return method.invoke(person, args);
				}
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
			return null;
		}
	}
	static class NonOwnerInvocationHandler implements InvocationHandler{
		Person person;
		
		public NonOwnerInvocationHandler(Person person) {
			this.person = person;
		}
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			try {
				if(method.getName().startsWith("get")) {
					return method.invoke(person, args);
				}else if(method.getName().startsWith("setGeekRating")) {
					return method.invoke(person, args);
				}else if(method.getName().startsWith("set")) {
					//내것 아니니까 수정 불가
					throw new IllegalAccessException();
				}
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
			return null;
		}
	}
	
	static Person getOwnerProxy(Person person) {
		return (Person)Proxy.newProxyInstance(//정적 메서드
				person.getClass().getClassLoader(),//클래스 로더
				person.getClass().getInterfaces(),//프록시에서 구현할 모든 인터페이스
				new OwnerInvocationHandler(person));//InvocationHandler 구현한 클래스
	}
	static Person getNonOwnerProxy(Person person) {
		return (Person)Proxy.newProxyInstance(
				person.getClass().getClassLoader(),
				person.getClass().getInterfaces(),
				new NonOwnerInvocationHandler(person));
	}
	
	
	static Person getPersonFromDatabase(String name) {
		return (Person)datingDB.get(name);
	}
	
	static void initializeDatabase() {
		Person joe = new PersonImpl();
		joe.setName("김자바");
		joe.setInterests("자동차, 컴퓨터, 음악");
		joe.setGeekRating(7);
		datingDB.put(joe.getName(), joe);

		Person kelly = new PersonImpl();
		kelly.setName("박자바");
		kelly.setInterests("웹쇼핑, 영화, 음악");
		kelly.setGeekRating(6);
		datingDB.put(kelly.getName(), kelly);
	}
	
	static HashMap<String, Person> datingDB = new HashMap<String, Person>();
	
	public static void main(String[] args) {
		initializeDatabase();
		drive();
	}

	static void drive() {
		Person joe = getPersonFromDatabase("김자바");
		
		Person ownerProxy = getOwnerProxy(joe);//프록시 생성
		
		System.out.println("이름은 " + ownerProxy.getName());
		ownerProxy.setInterests("볼링, 바둑");
		System.out.println("본인 프록시에 관심 사항을 등록합니다.");
		try {
			ownerProxy.setGeekRating(10);
		} catch (Exception e) {
			System.out.println("본인 프록시에 괴짜 지수를 매길 수 없습니다.");
		}
		System.out.println("괴짜 지수 " + ownerProxy.getGeekRating());

		Person nonOwnerProxy = getNonOwnerProxy(joe);// 프록시 생성
		
		System.out.println("이름은 " + nonOwnerProxy.getName());
		try {
			nonOwnerProxy.setInterests("볼링, 바둑");
		} catch (Exception e) {
			System.out.println("타인 프록시에는 관심 사항을 등록할 수 없습니다.");
		}
		nonOwnerProxy.setGeekRating(3);
		System.out.println("타인 프록시에 괴짜 지수를 매깁니다.");
		System.out.println("괴짜 지수 " + nonOwnerProxy.getGeekRating());

	}

}
이름은 김자바
본인 프록시에 관심 사항을 등록합니다.
본인 프록시에 괴짜 지수를 매길 수 없습니다.
괴짜 지수 7
이름은 김자바
타인 프록시에는 관심 사항을 등록할 수 없습니다.
타인 프록시에 괴짜 지수를 매깁니다.
괴짜 지수 1

핵심 정리

프록시 패턴을 사용하면 객체에 대리인을 내세워서 클라이언트 접근을 제어할 수 있다.

원격 프록시는 클라이언트와 원격 객체 사이 데이터 전달을 관리

가상 프록시는 생성비용이 큰 객체 접근을 제어(지연로딩)

보호 프록시는 권한을 확인하여 객체 접근을 제어

이외 도 다양한 프록시 변형이 존재한다. 캐시 서버도 프록시 일종이다.

자바에는 동적 프록시 기능이 내장되어 있다. 

+ Recent posts