해당 포스팅은 한빛 미디어 헤드퍼스트 디자인 패턴(에릭 프리먼, 엘리자베스 롭슨 저)를 통해 공부한 내용을 정리한 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
좋은 프로그래머는 자기 두뇌를 사용한다. 그러나 좋은 가이드라인은 모든 케이스를 고려해야만 하는 노력을 줄여준다.
(Francis Glassborow, 개발자)
Proxy Pattern
접근을 제어하고 관리 => 기본적으로 프록시가 하는 역할.
- 원격 객체의 프록시만 넘기면 다른 머신에서도 상태 확인 가능.
Proxy
- 진짜 객체를 대신하는 역할.
- 네트워크와 떨어져있는 클래스의 정보를 주고 받아야 함.
- 서비스 역할 필요.
- 네트워크로 들어오는 요청 수용
- 프록시 객체의 레퍼런스를 받아오는 기능 필요
원격 프록시의 역할
- 원격 객체의 로컬 대변자 역할. => 어떤 메소드를 호출하면 다른 원격 객체에서 그 메소드 호출을 전달하는 객체.
- 원격 객체 => JVM의 힙에 존재하는 객체(다른 주소 공간에서 돌아가고있는 객체)
- 네트워크 통신과 같은 low level 작업은 프록시 객체에서 관리.
모니터링 코드에 원격 프록시 추가하기.
- 다른 JVM에 들어있는 객체의 메소드를 호출할 수 없음.
- RMI(Remote Method Invocation)를 이용하여 프록시 만들기
💡 모니터링 코드란
이전 State Pattern에서 예시를 통해 든 GumballMachine에서의 상태를 확인하는 코드
원격 메소드 기초
- 로컬 객체의 메소드를 호출하면 그 요청을 원격 객체에 전달해주는 시스템을 디자인.
- 통신을 처리해주는 보조 객체가 필요하고 보조 객체를 사용.
- 클라이언트는 로컬 객체의 메소드만 알면 됨.
- 클라이언트 보조 객체의 메소드를 호출 => 서비스 보조 객체 호출(1)
- 진짜 원격 서비스인 척하는 객체 => 클라이언트 보조 객체
- 클라이언트 보조 객체는 서버에 연락을 취하고(2) 메소드 호출에 관한 정보를 전달하고 서버로부터 리턴되는 정보를 기다림.
- 서버 보조 객체는 소켓으로 연결된 보조 객체로부터 요청을 받아오고(2) 호출 정보를 해석해서 진짜 메소드 호출(3)
- 이후 반환되는 과정은 3 -> 2 -> 1 순으로 전달.
RMI 개요
- 클라이언트와 서비스 보조 객체를 만들어주는 JAVA API
- 네트워킹 및 입출력 관련 코드를 작성하지 않아도 JVM 메소드를 호출하듯 원격 메소드 호출 가능
- 클라이언트보조 객체 => 스텁(stub)
서버 보조 객체 => 스켈레톤(skeleton)
원격 서비스 만들기
원격 인터페이스 만들기
import java.rmi.*;
public interface MyRemote extends Remote {
public String sayHello() throws RemoteException;
}
- 클라이언트가 원격으로 호출할 메소드 정의.
- 인터페이스를 서비스의 클래스 형식으로 사용
- java.rmi.Remote 확장 및 Remote 클래스 상속
- 모든 메소드를 RemoteException을 던지도록 선언.
- 원격 메소드의 인자나 반환 값은 반드시 primitive 또는 Serializble 형식으로
서비스 구현 클래스 만들기.
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class GumballMachine extends UnicastRemoteObject implements GumballMachineRemote {
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State winnerState;
State state = soldOutState;
int count = 0;
String location;
public GumballMachine(String location, int numberGumballs) throws RemoteException {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
winnerState = new WinnerState(this);
this.location = location;
this.count = numberGumballs;
if (numberGumballs > 0) {
state = noQuarterState;
}
}
}
import java.rmi.Naming;
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();
}
}
}
- 실제 작업 처리 클래스
- UnicastRemoteObject 확장.
- RemoteException을 선언하는 생성자 구현
- 서비스를 RMI 레지스트리에 등록
RMI 레지스트리 구성
- 클라이언트는 레지스트리로부터 프록시(스텁, 클라이언트 보조 객체)를 받아감.
원격 서비스 실행하기
- 서비스 객체 실행
Proxy Pattern
특정 객체로의 접근을 제어하는 대리인 제공 => 프록시
프록시에 접근을 제어하는 방법
- 원격 프록시를 써서 원격 객체로의 접근을 제어
- 가상 프록시를 써서 생성하기 힘든 자원으로의 접근을 제어
- 보호 프록시를 써서 접근 권한이 필요한 자원으로의 접근을 제어
- RealSubject와 Proxy 인터페이스를 제공하는 Subject 인터페이스 존재
- 두 객체는 똑같은 인터페이스를 구현하기에 RealSubject가 들어가야할 자리에 Proxy를 대신 넣음
- 진짜 작업은 RealSubject가 처리. 대변인 역할이자 접근 제어
원격 프록시와 가상 프록시 비교하기
원격 프록시
- 다른 JVM에 들어있는 객체의 대리인에 해당하는 로컬 객체
가상 프록시
- 생성하는데 많은 비용이 드는 RealSubject를 대신하는 프록시 객체
보호 프록시 만들기
- java.lang.reflect 패키지 안에 프록시 기능이 내장
- 즉석에서 하나 이상의 인터페이스를 구현하고 지정한 클래스에 메소드 호출을 전달하는 프록시 클래스 만들기 가능.
- 진짜 프록시 클래스는 실행 중에 생성되므로 이러한 자바 기술을 동적 프록시라함.
- 자바에서 Proxy 클래스를 생성해주므로 Proxy 클래스에게 무슨 일을 해야하는지 알려줄 방법이 필요함.
- InvocationHandler에 넣어주기
- 프록시에 호출되는 모든 메소드에 응답.
- 클라이언트에서 아무 메소드나 마음대로 호출할 수도 있기에 보호 프록시가 필요
- 접근 권한을 바탕으로 객체로의 접근을 제어하는 프록시
'기타 > 디자인패턴' 카테고리의 다른 글
[Design Pattern] Combined Pattern (0) | 2024.06.10 |
---|---|
[Design Pattern] State Pattern (0) | 2024.06.09 |
[Design Pattern] Iterator Pattern & Composite Pattern (0) | 2024.06.09 |
[Design Pattern] Template Method Pattern (0) | 2024.06.05 |
[Design Pattern] Adapter Pattern (0) | 2024.06.04 |