Proxy Pattern
프록시 패턴은 surrogate 와 다른 객체의 접근에 대한 통제를 할 수 있는 placeholder를 제공한다.
- 객체의 대리자를 이용하여 다른 객체로의 접근을 통제하는 패턴
- 대리 객체를 통해 원래 객체의 작업을 대신 수행
- 복잡한 작업과 단순한 작업을 나누어서 처리
- 원래의 기능을 수행하면서 부가적인 기능을 수행할 때 유용
문제점)
- 객체 생성에 비용(시간, 자원)이 많이 드는 Heavy object 가 있다.
- 프로그램이 종료될 때까지 한 번도 사용되지 않으면 시간, 자원의 낭비가 있다.
Key Idea) Heavy object를 대리할 light object를 생성
- Heavy object와 동일한 인터페이스를 가지는 proxy (대리 객체)
- Heavy object 대신 proxy class 를 생성한다.
- 프록시의 메소드가 호출될 때 (실제 heavy object가 필요할 때) 프록시는 heavy object를 생성하고 작업을 위임한다.
위임을 위해 프록시도 동일한 인터페이스를 구현한다.
프록시는 RealSubject를 위임한다.
원격이 될 수 있고 생성에 리소스 비용이 많이 들거나 보안이 필요한 또 다른 객체(object)에 접근(access)을 통제하는 대리 객체를 생성하기 위해 프록시 패턴을 사용한다.
Proxy:
doAction() {
사전작업;
realSubject.doAction();
사후작업;
}
Proxy
public class Proxy implements Subject
{
//composition of null real subject
private Subject s = null; //한번만 생성하면 된다.
public void doAction() { // 인터페이스 메소드
if (s == null)
s = new Subject(); // 실제 객체 생성
s.doAction(); // 실제 객체에게 위임
}
}
Client
// proxy 생성.
Proxy p = new Proxy();
// subject method 호출시
// real subject 생성.
if (cond) p.doAction();
Proxy Pattern 용도
Remote Proxy
Remote 프록시는 다른 JVM에 있는 객체의 로컬 대표자 역할을 한다.
Virtual Proxy
가상 프록시는 생성에 비용이 많이 들 수 있는 객체의 대표자 역할을 한다.
Virtual Proxy는 필요할 때까지 개체 생성을 연기하는 경우가 많습니다.
프록시가 RealSubject 에게 직접 요청
Firewall Proxy
방화벽 시스템 주로 위치
네트워크 자원 접근이나 “bad” clients로 부터 the subject를 보호하는데에 컨트롤
Smart Reference Proxy
Subject 가 객체에 대한 참조 수 계산과 같이 참조하는 actions를 추가적으로 제공
Caching Proxy
content management and publishing systems 뿐만 아니라 웹 서버 프록시에 사용
캐싱 프록시는 비용이 많이 드는 작업 결과를 위한 임시 저장소를 제공한다.
여러 클라이언트가 결과를 공유하여 계산을 줄이고 네트워크 지연시간을 줄일 수 있습니다.
Synchronization Proxy
멀티 쓰레드로 부터 주제에 안전한 접근을 제공한다.
Complexity Hiding Proxy
복잡성을 숨기고 클래스들의 복잡한 셋에 접근을 제어한다.
The Facade Proxy 라고 부르지만 단지 대체 인터페이스를 제공
Copy-On-Write Proxy
클라이언트에 필요할 때까지 개체 복사를 지연하여 개체 복사를 제어합니다.
ex) Java Proxy API
Proxy, InvocationHandler : java.lang.reflect
Protection Proxy 구현
InvocationHandler
proxy에 대한 호출이 오면 메소드 호출을
real subject 메소드 호출로
변환함 (하는 규칙을 가짐)
생성시 구성
invoke 메소드
변환규칙 (호출 규칙)
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException
{
try {
if(method.getName().startsWith("get")){
return method.invoke(person,args);
}...
}
- 프록시 메소드를 real object에 대한 호출로 변환한다.
- proxy.getName()), proxy에 의해 메소드가 불려진다.
- InvocationHandler 에 의해 invoke() 를 부른다.
- Method 클래스는 reflection API 이다. method는 프록시에 의해 getName()이 불려진다.
- return method.invoke(person,args);
- invoke가 Real Subject의 메소드를 부른다. (실 객체의 함수를 부른다.)
프록시 패턴 구현 예제
인터페이스 : PersonBean
클래스 상속 : PersonBeanImpl, Proxy
PersonBeanImpl : PersonBean 의 구현 객체 (Heavy object)
Proxy : PersonBean을 구성 (light object)
소스코드
PersonBean
package Proxy.pattern;
public interface PersonBean
{
String getName();
String getInterests();
int getRating();
int getHotOrNotRating();
void setName(String name);
void setInterests(String interests);
void setRating(int rating);
void setHotOrNotRating(int rating);
}
PersonBeanImpl
package Proxy.pattern;
public class PersonBeanImpl implements PersonBean
{
private String name;
private String gender;
private String interests;
private int rating=0;
private int ratingCount=0;
@Override
public String getName() {
return name;
}
@Override
public String getInterests() {
return interests;
}
@Override
public int getRating() {
return rating;
}
@Override
public int getHotOrNotRating() {
System.out.println(rating + " " +ratingCount);
if(ratingCount == 0)return 0;
return (rating/ratingCount);
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void setInterests(String interests) {
this.interests = interests;
}
@Override
public void setRating(int rating) {
this.rating += rating;
}
@Override
public void setHotOrNotRating(int rating) {
this.rating += rating;
ratingCount++;
}
}
Proxy
package Proxy.pattern;
public class Proxy implements PersonBean
{
PersonBean personBean;
public Proxy(PersonBean personBean) {
this.personBean = personBean;
}
private String name;
private String gender;
private String interests;
private int rating=0;
private int ratingCount=0;
@Override
public String getName() {
return (personBean != null) ? personBean.getName() : name;
}
@Override
public String getInterests() {
return (personBean != null) ? personBean.getInterests() : interests;
}
@Override
public int getRating() {
return (personBean != null) ? personBean.getRating() : rating;
}
@Override
public int getHotOrNotRating() {
if(personBean != null){
return personBean.getHotOrNotRating();
}else {
if (ratingCount == 0) return 0;
return (rating / ratingCount);
}
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void setInterests(String interests) {
this.interests = interests;
}
@Override
public void setRating(int rating) {
this.rating += rating;
}
@Override
public void setHotOrNotRating(int rating) {
this.rating += rating;
ratingCount++;
}
}
Client
package Proxy.pattern;
public class Client {
public static void main(String[] args) {
// 객체 생성
PersonBean hong = new PersonBeanImpl();
hong.setName("길동"); hong.setInterests("Bike, music"); hong.setRating(7);
Proxy p = new Proxy(hong);
System.out.println("proxy name is : "+p.getName());
}
}
책 참고 : Head First Design Patterns
댓글