티스토리 뷰
728x90
1. 프록시 패턴
- 특정 객체에 대한 접근을 제어하거나 기능을 추가할 수 있는 패턴.
- 초기화지연, 접근 제어, 로깅, 캐싱등 다양하게 사용 가능함.
2. 패턴 적용 전 코드
- 아래와 같이 게임을 시작하는 메서드가 존재함.
- 게임을 시작하는 메서드를 수정하지 않고 프록시 패턴을 적용하여 메서드가 실행되는 소요시간을 계산하려함.
public class GameService {
public void gameStart() {
System.out.println("이 자리에 오신 여러분 진심으로 환영합니다.");
}
}
public class Client {
public static void main(String[] args) {
GameService gameService = new GameService();
gameService.gameStart();
}
}
3. 패턴 적용 후 코드
3.1. 상속을 이용한 방식
- 프록시 패턴을 사용하고자 하는 클래스가 인터페이스를 사용하지 않았다면 상속을 이용하여 적용한다.
public class GameServiceProxy extends GameService {
@Override
public void gameStart() {
long start = System.currentTimeMillis();
super.gameStart();
System.out.println(System.currentTimeMillis() - start);
}
}
public class ProxyClient {
public static void main(String[] args) {
GameService gameService = new GameServiceProxy();
gameService.gameStart();
}
}
3.2. 인터페이스를 이용한 방식
- 인터페이스를 이용하여 구현하면 유연한 개발이 가능해짐. (활용도가 높아짐)
public interface GameService {
void gameStart();
}
public class DefaultGameService implements GameService {
@Override
public void gameStart() {
System.out.println("이 자리에 오신 여러분들 환영합니다.");
}
}
public class GameServiceProxy implements GameService {
private final GameService gameService;
public GameServiceProxy(GameService gameService) {
this.gameService = gameService;
}
@Override
public void gameStart() {
long start = System.currentTimeMillis();
this.gameService.gameStart();
System.out.println(System.currentTimeMillis() - start);
}
}
public class ProxyClient {
public static void main(String[] args) {
GameService gameService = new GameServiceProxy(new DefaultGameService());
gameService.gameStart();
}
}
4. 장점 및 단점
4.1. 장점
- 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있다.
- 기존 코드가 해야 할 일만 남겨 둘수 있다.
- 기능 추가 및 초기화 지연 등으로 다양하게 활용할 수 있다.
4.2. 단점
- 코드가 복잡해진다.
5. 실 사용 예시
5.1. 자바 - Dynamic Proxy
5.1.1. 프록시 실제 구현시 문제점
- 인터페이스를 직접 구현해야한다.
- 인터페이스의 모든 구현체를 구현(override)해야 한다. (특정 메서드만 프록시 사용 불가)
- 프록시 클래스 내에 중복이 발생 할 수 있다. (메서드 마다 하는일이 비슷할 시 중복 발생 가능)
5.1.2. Dynamic Proxy
- 런타임 시점에 인터페이스를 구현하는 클래스 또는 인스턴스를 만드는 기술을 의미한다.
- 그러나 Dynamic Proxy의 경우 인터페이스를 통해야만 구현이 가능하다.
public class ProxyClient {
public static void main(String[] args) {
ProxyClient proxyClient = new ProxyClient();
GameService gameService = proxyClient.dynamicProxy();
gameService.gameStart();
}
public GameService dynamicProxy() {
GameService gameServiceProxy = getGameServiceProxy(new DefaultGameService());
return gameServiceProxy;
}
public GameService getGameServiceProxy(GameService target) {
return (GameService) Proxy.newProxyInstance(
this.getClass().getClassLoader(), // 프록시를 정의할 클래스 로더
new Class[] { GameService.class }, // 프록시 클래스가 구현하고자하는 인터페이스 목록
(proxy, method, args) -> { // 프록시의 메서드가 호출될때 처리되는 구현부
System.out.println("Hello Dynamic Proxy");
method.invoke(target, args);
return null;
}
);
}
}
5.1.3. CGLib (Code Generator Library)
- 클래스 상속을 통하여 Proxy를 동적으로 구현해 주므로 인터페이스를 사용하지 않고 Proxy를 사용할 수 있게 해준다.
- Spring AOP, Hibernate 등에서 CGLib를 사용한다.
- 그러나, 상속을 이용한 Proxy이므로 상속이 불가(final, private 생성자, abstract)한 클래스의 경우에는 사용이 불가하다.
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
public class ProxyClient {
public static void main(String[] args) {
ProxyClient proxyClient = new ProxyClient();
GameService cgLibProxyGameService = proxyClient.cgLibProxy();
cgLibProxyGameService.gameStart();
}
public GameService cgLibProxy() {
MethodInterceptor interceptor = new MethodInterceptor() {
final GameService gameService = new DefaultGameService();
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("game start 이전");
Object invoke = method.invoke(gameService, objects);
System.out.println("game start 이후");
return invoke;
}
};
GameService proxyGameService = (GameService) Enhancer.create(GameService.class, interceptor);
return proxyGameService;
}
}
728x90
'디자인 패턴' 카테고리의 다른 글
14. 커맨드 패턴 (0) | 2022.02.10 |
---|---|
13. 책임연쇄패턴 (0) | 2022.02.10 |
11. 플라이 웨이트 패턴 (0) | 2022.02.10 |
10. 퍼사드 패턴 (0) | 2022.02.10 |
09. 데코레이터 패턴 (0) | 2022.02.10 |
댓글