import java.lang.reflect.*;
public class OwnerInvocationHandler implements InvocationHandler {
PersonBean person;
public OwnerInvocationHandler(PersonBean person) {
this.person = person;
}
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {
try {
if (method.getName().startsWith("get")) {
return method.invoke(person, args);
} else if (method.getName().equals("setHotOrNotRating")) {
throw new IllegalAccessException();
} else if (method.getName().startsWith("set")) {
return method.invoke(person, args);
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
Client 클래스 : Main 함수에서 테스트
public class MatchMakingTestDrive {
public static void main(String[] args) {
MatchMakingTestDrive test = new MatchMakingTestDrive();
test.drive();
}
public MatchMakingTestDrive() {
initializeDatabase();
}
public void drive() {
PersonBean joe = getPersonFromDatabase("Joe Javabean");
PersonBean ownerProxy = getOwnerProxy(joe);
System.out.println("Name is " + ownerProxy.getName());
ownerProxy.setInterests("bowling, Go");
System.out.println("Interests set from owner proxy");
try {
ownerProxy.setHotOrNotRating(10);
} catch (Exception e) {
System.out.println("Can't set rating from owner proxy");
}
System.out.println("Rating is " + ownerProxy.getHotOrNotRating());
PersonBean nonOwnerProxy = getNonOwnerProxy(joe);
System.out.println("Name is " + nonOwnerProxy.getName());
try {
nonOwnerProxy.setInterests("bowling, Go");
} catch (Exception e) {
System.out.println("Can't set interests from non onwer proxy");
}
nonOwnerProxy.setHotOrNotRating(3);
System.out.println("Rating set from non owner proxy");
System.out.println("Rating is " + nonOnwerProxy.getHotOrNotRating());
}
// subject의 classloader, subject의 interface,
// 그리고 subject의 InvocationHandler객체를 인자로 받아서 프록시 객체를 생성
PersonBean getOwnerProxy(PersonBean person) {
return (PersonBean)Proxy.newProxyInstance(person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person));
}
// getNonOwnerProxy(PersonBean person) 등 추가적으로 구현
}
조금 더 직관적인 예제 : Displaying Image
Subject : Image Interface
public interface Image {
void displayImage();
}
RealSubject : RealImage class
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
@Override
public void displayImage() {
System.out.println("Displaying " + fileName);
}
}
Proxy : ProxyImage class
public class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void displayImage() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.displayImage();
}
}
Client : ProxyMain class
public class ProxyMain {
public static void main(String[] args) {
Image image1 = new ProxyImage("test1.png");
Image image2 = new ProxyImage("test2.png");
image1.displayImage();
System.out.println();
image2.displayImage();
}
}
결과
Loading test1.png Displaying test1.png
Loading test2.png Displaying test2.png
ProxyMain에서 RealImage에 직접 접근하지 않고 ProxyImage 객체를 생성하여 대신 시키게 됨
Proxy는 displayImage() 메서드를 호출하고 그 반환값을 Main에 반환함
Proxy의 종류
원격 프록시
클라이언트와 원격 객체 사이의 데이터 전달을 관리해줌
가상 프록시
인스턴스를 생성하는데 비용이 많이 드는 객체에 대한 접근을 제어
보호 프록시
호출한 쪽의 권한에 따라 객체에 있는 메소드에 대한 접근을 제어
결론
Proxy Pattern은 어떤 다른 객체로 접근하는 것을 통제하기 위해서 그 객체의 대리자나 자리표시자(placeholder)의 역할을 하는 객체를 제공하는 구조 패턴(Structural Pattern)
비슷한 패턴들과의 비교
Decorator : 다른 객체를 감싸서 새로운 행동을 추가해줌
Facade : 여러 객체를 감싸서 인터페이스를 단순화 시킴
Adapter : 다른 객체를 감싸서 다른 인터페이스를 제공
Proxy : 다른 객체를 감싸서 접근을 제어
장점
사이즈가 큰 객체가 로딩되기 전에도 프록시를 통해 참조할 수 있음
로컬에 있지 않고 떨어져 있는 객체를 사용할 수 있음
원래 객체에 접근하는 것에 대해 사전 처리를 할 수 있음
단점
객체를 생성할 때 한 단계 거치게 되므로, 빈번한 객체 생성이 필요한 경우 성능이 저하될 수 있음