어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 패턴

로컬 환경에 존재하면서, 원격객체에 대하여 대변자 역할을 하는 객체를 원격 프록시라고 한다.

클라이언트 객체(GumballMonitor)에서는 원격객체(GumballMachine)의 메소드를 호출하는 것처럼 행동하지만, 실제로는 로컬 힙에 들어있는 원격객체 모양과 비슷한 '프록시'객체의 메소드를 호출하고 있는 것이다.
네트워크 통신과 관련된 저수준 작업은 이 프록시 객체에서 처리해준다.

클라이언트 객체 입장에서는 메소드 호출이 어디로 전달되었었는지, 어디에서 왔는지 전혀 알 수 없다.
GumballMachineRemote.java
| New |
|---|
| {code} import java.rmi.*; |
public interface GumballMachineRemote extends Remote {
public int getCount() throws RemoteException;
public String getLocation() throws RemoteException;
public State getState() throws RemoteException;
}
|
*State.java*
||Before||After||
|
public interface State {
public void insertQuarter();
public void ejectQuarter();
public void turnCrank();
public void dispense();
}
|
import java.io.*;
public interface State extends Serializable {
public void insertQuarter();
public void ejectQuarter();
public void turnCrank();
public void dispense();
}
|
*NoQuarterState.java*
||Before||After||
|
public class NoQuarterState implements State {
GumballMachine gumballMachine;
}
|
public class NoQuarterState implements State {
transient GumballMachine gumballMachine;
}
|
*GumballMachine.java*
||Before||After||
|
public class GumballMachine {
public GumballMachine(String locatrion, int numberGumballs) {
// 생성자코드
}
public int getCount() {
return count;
}
public State getState(){
return state;
}
public String getLocation(){
return location;
}
}
|
import java.rmi.*;
import java.rmi.server;
public class GumballMachine extends UnicastRemoteObject implements GumballMachineRemote {
public GumballMachine(String locatrion, int numberGumballs) throws RemoteException {
// 생성자코드
}
public int getCount() {
return count;
}
public State getState(){
return state;
}
public String getLocation(){
return location;
}
}
|
*GumballMachineTestDrive.java*
||Before||After||
|
public class GumballMachineTestDrive {
public static void main(String[] args) {
int count = 0;
if (args.length < 2) {
System.out.println("GumballMachine <name> <inventory>");
System.exit(1);
}
count = Integer.parseInt(args[1]);
GumballMachine gumballMachine = new GumballMachine(args[0], count);
GumballMonitor monitor = new GumballMonitor(gumballMachine);
// 기타코드
monitor.report();
}
}
|
import java.rmi.*;
public class GumballMachineTestDrive {
public static void main(String[] args) {
GumballMachineRemote gumballMachine = null;
int count;
if (args.length < 2) {
System.out.println("GumballMachine <name> <inventory>");
System.exit(1);
}
try {
count = Integer.parseInt(args[1]);
gumballMachine = new GumballMachine(args[0], count);
Naming.rebind("//" + args[0] + "/gumballmachine", gumballMachine);
} catch (Exception e) {
e.printStackTrace();
}
}
}
|
*GumballMonitor.java*
||Before||After||
|
public calss GumballMonitor {
GumballMachine machine;
public GumballMonitor(GumballMachine machine) {
this.machine = machine;
}
public void report() {
System.out.println("뽑기 기계위치:"+machine.getLocation());
System.out.println("현재 재고:"machine.getCount()" 개");
System.out.println("현재 상태:"+machine.getState());
}
}
|
import java.rmi.*;
public calss GumballMonitor {
GumballMachineRemote machine;
public GumballMonitor(GumballMachineRemote machine) {
this.machine = machine;
}
public void report() {
try{
System.out.println("뽑기 기계위치:"+machine.getLocation());
System.out.println("현재 재고:"machine.getCount()" 개");
System.out.println("현재 상태:"+machine.getState());
} catch(RemoteException e){
e.printStackTrace();
}
}
}
|
h2. 3. 가상프록시
h3. 3.1 가상프록시 개요
생성하는데 많은 비용이 드는 객체를 대신하는 역할을 한다. 실제로 진짜 객체가 필요하게 되기 전까지 객체의 생성을 미루게 해 주는 기능을 제공하거나, 객체 생성 전, 또는 생성도중에 객체를 대신하기도 한다. 객체 생성이 완료되고나면 프록시에서 RealSubject에 요청을 직접 전달한다.
h3. 3.2 CD커버 프로그램 만들기.
CD커버를 보여주는 프로그램을 만들기로 가정한다.
CD타이틀 목록을 선택하면, 선택한 CD커버 이미지를 네트워크를 통해서 불러오도록한다.
하지만 네트웍의 연결속도에 따라 약간의 시간이 걸릴수 있으므로, 이미지를 불러오는동안 화면에 무언가를 보여주거나, 애플리케이션의 전체 작동이 멈추어서는 안된다.
h3. 3.3 ImageProxy작동방법
# ImageProxy에서는 우선 ImageIcon을 생성하고 네트워크 URL로부터 이미지를 불러온다.
# 이미지를 가져오는 동안에는 "Loading CD cover, please wait.."라는 메시지를 화면에 표시한다.
# 이미지 로딩이 끝나면 paintIcon(), getWidth(), getHeight()를 비론한 모든 메소드 호출을 이미지 아이콘 객체에게 넘김.
# 사용자가 새로운 이미지를 요청하면 프록시를 새로 만들고 위의 과정을 새로 진행한다.
h2. 4. 보호프록시
h3. 4.1 보호프록시 개요
접근권한을 바탕으로 객체에 대한 접근을 제어하는 프록시
자바에 프록시 기능이 내장되어 있으며, java.lang.reflect패키지를 이용하여 만들수 있음
h3. 4.2 결혼정보서비스 프로그램
해당 프로그램은 결혼정보서비스에 가입한 고객에 대하여,
고객은 자신의 정보를 수정가능하게 할수 있으나, 타인의 정보는 수정하지 못하게하고, 타인의 선호도는 평가할 수 있으나, 자신의 선호도는 평가할수 없게 하는 등의 선택적으로 접근을 제어한다.
h3. 4.3 소스보기
*MatchMakingTestDrive.java*
import java.lang.reflect.*;
import java.util.*;
public class MatchMakingTestDrive {
Hashtable datingDB = new Hashtable();
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 owner proxy");
}
nonOwnerProxy.setHotOrNotRating(3);
System.out.println("Rating set from non owner proxy");
System.out.println("Rating is " + nonOwnerProxy.getHotOrNotRating());
}
PersonBean getOwnerProxy(PersonBean person) {
return (PersonBean) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person));
}
PersonBean getNonOwnerProxy(PersonBean person) {
return (PersonBean) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new NonOwnerInvocationHandler(person));
}
PersonBean getPersonFromDatabase(String name) {
return (PersonBean)datingDB.get(name);
}
void initializeDatabase() {
PersonBean joe = new PersonBeanImpl();
joe.setName("Joe Javabean");
joe.setInterests("cars, computers, music");
joe.setHotOrNotRating(7);
datingDB.put(joe.getName(), joe);
PersonBean kelly = new PersonBeanImpl();
kelly.setName("Kelly Klosure");
kelly.setInterests("ebay, movies, music");
kelly.setHotOrNotRating(6);
datingDB.put(kelly.getName(), kelly);
}
}
*OwnerInvocationHandler.java*
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;
}
}
*NonOwnerInvocationHandler.java*
import java.lang.reflect.*;
public class NonOwnerInvocationHandler implements InvocationHandler {
PersonBean person;
public NonOwnerInvocationHandler(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")) {
return method.invoke(person, args);
} else if (method.getName().startsWith("set")) {
throw new IllegalAccessException();
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
h2. 문서에 대하여
* 참고사이트 : http://sourcemaking.com/design_patterns
* 이 문서는 [HeadFirst Design Patterns|http://book.naver.com/bookdb/book_detail.php?bid=1882446]을 정리한 내용 입니다.
* 이 문서는 [오라클클럽|http://www.gurubee.net] [자바 웹개발자 스터디|제3차 자바 웹개발자 스터디] 모임에서 작성하였습니다.
* 이 문서를 다른 블로그나 홈페이지에 퍼가실 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^\^