(인프런) 코딩으로 학습하는 GoF의 디자인 패턴 - 백기선, 강의를 보고 정리한 글입니다.
코드는 GitHub 에 있습니다
#1. 객체 생성 관련 패턴 |
#2. 구조 관련 패턴 |
#3. 행동 관련 패턴 |
||
✔️ 프록시 패턴이란 (Proxy Patterns)
특정 객체에 대한 접근을 제어하거나 기능을 추가할 수 있는 패턴입니다.
- 프록시(Proxy)란 대리자, 대변인의 의미를 가지고 있습니다. 즉 누군가를 대신해서 그 역할을 수행하는 존재이고 프로그래밍에서도 같은 의미로 프록시에게 어떠한 일을 대신 시키는 것을 말합니다.
- 프록시 패턴은, 어떠한 객체에 대해 접근하기 전에 프록시 객체를 거쳐 "초기화 지연, 접근 제어, 로깅, 캐싱" 등 다양한 기능을 응용해서 사용할 수 있도록 하는 구조입니다.
- 즉, 어떠한 객체에대해 직접 참조하지 않고 무엇인가의 행위를 그 전에 하고싶을 때 프록시 패턴의 구조를 사용하면 유용하다고 볼 수 있을 것 같습니다.
✔️ 프록시 패턴 사용 예시
단순하게 Proxy Pattern 의 개념만 적용한 예시 입니다.
- 아래처럼 어떤 로직을 수행하는 Service 객체가 존재할 때, 이 객체가 수행하는 로직에대해 시간을 측정하고 싶다면
- startGame() 이라는 메서드를 직접 수정해서 "시간 측정"에 대한 기능을 추가할 수 있습니다.
public class Client {
public static void main(String[] args) {
GameService gameService = new GameService();
gameService.startGame();
}
}
public class GameService {
public void startGame() {
//엄청난 비지니스 로직
}
}
하지만 그렇게 요구사항이 생길 때 마다 로직을 직접적으로 수정하고 변경할 수 없을때도 존재하고(Side-Effect), 객체지향 원칙 중 OCP 에도 위반됩니다.
따라서 우리는 프록시 패턴을 적용하여 "시간 측정"이라는 기능을 대신 수행할 수 있습니다.
[프록시 패턴 적용]
- Client는 객체를 직접 접근하는게 아닌, 프록시 객체를 통해 접근합니다.
- 프록시 객체는 컨택스트 객체의 기능을 참조하면서, "시간 측정" 이라는 기능을 대신 수행합니다.
- 이로써 기존의 컨택스트 객체의 starGame() 은 전혀 수정하지않고 프록시 객체가 대신 원하는 기능을 수행하는 구조가 되었습니다.
public class Client {
public static void main(String[] args) throws InterruptedException {
GameService gameService = new GameServiceProxy(new DefaultGameService());
gameService.startGame();
}
}
public class GameServiceProxy extends GameService {
@Override
public void startGame() throws InterruptedException {
long before = System.currentTimeMillis();
super.startGame();
System.out.println(System.currentTimeMillis() - before);
}
}
[상속이 아닌 위임]
- 상속이 아닌 위임의 방식으로 프록시 패턴을 적용시킬 수 있습니다.
//Client
public class Client {
public static void main(String[] args) throws InterruptedException {
GameService gameService = new GameServiceProxy(new DefaultGameService());
gameService.startGame();
}
}
//인터페이스로 수정해 더 유연하게 코드를 변경할 수 있음 (복잡하지만)
public interface GameService {
void startGame() throws InterruptedException;
}
//Context Class
public class DefaultGameService implements GameService{
@Override
public void startGame() throws InterruptedException {
//엄청난 비지니스 로직 매우매우 오래걸림
}
}
- 상속이 아닌 위임을 통해 ocp 를 지키고 확장성을 증가시킬 수 있습니다.
public class GameServiceProxy implements GameService {
private GameService gameService;
public GameServiceProxy(GameService gameService) {
this.gameService = gameService;
}
@Override
public void startGame() throws InterruptedException {
long before = System.currentTimeMillis();
gameService.startGame();
System.out.println(System.currentTimeMillis() - before);
}
}
프록시로 초기화 지연 사용하기
- 상속이 아닌 위임의 구조로 프록시 패턴을 적용하면 초기화 지연기능 또한 쉽게 구현할 수 있습니다.
public class Client {
public static void main(String[] args) throws InterruptedException {
GameService gameService = new GameServiceProxy();
gameService.startGame();
}
}
public class GameServiceProxy_Plus implements GameService {
private GameService gameService;
@Override
public void startGame() throws InterruptedException {
long before = System.currentTimeMillis();
//이런식의 lazy 로딩도 가능함
if (this.gameService == null) {
this.gameService = new DefaultGameService();
}
gameService.startGame();
System.out.println(System.currentTimeMillis() - before);
}
}
✔️ 프록시 패턴 장단점
장점
- 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있다.
- 기존 코드는 해야 하는 일만 유지할 수 있다.
- 기능 추가 및 초기화 지연 등으로 다양하게 활용할 수 있다.
- 실제 객체의 public, protected 메소드를 숨기고 인터페이스를 통해 노출시킬 수 있다.
단점
- 객체를 생성할 때 한 단계를 거치게 되므로, 빈번한 객체 생성이 필요한 경우 성능이 저하될 수 있다.
- 프록시 내부에서 객체 생성을 위해 스레드가 생성, 동기화가 구현되어야 하는 경우 성능이 저하될 수 있다.
- 로직이 난해해져 가독성이 떨어질 수 있다.
✔️ JAVA 와 Spring 에서의 프록시 패턴
자바
- 다이나믹 프록시
- java.lang.reflect.Proxy
스프링
- 스프링 AOP
'Java > Design Pattern' 카테고리의 다른 글
[디자인 패턴] 행동 패턴 - 인터프리터 패턴 (Interpreter Pattern) (0) | 2023.03.31 |
---|---|
[디자인 패턴] 행동 패턴 - 커맨드 패턴 (Command Pattern) (0) | 2023.03.29 |
[디자인 패턴] 구조 패턴 - 플라이웨이트 패턴 (Flyweight Pattern) (0) | 2023.03.15 |
[디자인 패턴] 구조 패턴 - 퍼사드 패턴 (Facade Pattern) (0) | 2023.03.09 |
[디자인 패턴] 구조 패턴 - 데코레이터 패턴 (Decorator Pattern) (0) | 2023.03.07 |