프록시 패턴(Proxy Pattern)
- Proxy는 대리자, 대변인 이라는 뜻이다.
- 프록시 패턴은 구조패턴(Structural Pattern) 중 하나이다.
- 구조 패턴은 작은 클래스들을 상속과 합성을 이용하여 더 큰 클래스를 생성하는 방법을 제공하는 패턴이다.
- 인터페이스나 구현을 복합하는 것이 아니라 객체를 합성하는 방법을 제공한다.
이는 컴파일 단계에서가 아닌 런타임 단계에서 복합 방법이나 대상을 변경할 수 있다는 점에서 유연성을 갖는다.
프록시의 주요 기능
프록시를 통해서 할 수 있는 일은 크게 2가지로 나뉜다.
1. 접근 제어 (권한에 따른 접근 차단, 캐싱 등)
2. 부가 기능 추가 (다른 로직을 추가하는 등의 부가 기능을 수행, 예) 실행 시간을 측정해서 로그를 남긴다.)
* GOF 디자인 패턴에서는 의도에 따라서 프록시 패턴과, 데코레이터 패턴으로 구분한다.
- 프록시 패턴 : 접근 제어가 목적
- 데코레이터 패턴 : 새로운 기능 추가가 목적
프록시 패턴의 장점
- 제어의 흐름을 변경하거나 다른 로직을 수행하기 위해 사용한다.
- 객체에 대하여 접근할 때에 Wrapper Class를 두어 접근에 대한 통제를 위해 사용된다.
코드
* 더하기 빼기 계산기 로직을 구현 예제 (프록시 패턴을 이용해서 계산 시간을 출력)
- 계산기 interface
public interface Calculator {
public int plus(int left, int right);
public int subtract(int left, int right);
}
- 계산기 interface 구현 service
public class CalculatorService implements Calculator{
private int result;
@Override
public int plus(int left, int right) {
result = left + right;
return result;
}
@Override
public int subtract(int left, int right) {
result = left - right;
return result;
}
}
- 프록시 패턴을 사용하지 않은 테스트
@SpringBootTest
class CalculatorServiceTest {
@Test
void calculatorService() {
Calculator calculatorService = new CalculatorService();
int left = 10000;
int right = 5000;
int plusResult = calculatorService.plus(left, right);
int subtractResult = calculatorService.subtract(left, right);
assertEquals(plusResult, left+right);
assertEquals(subtractResult, left-right);
}
}
- 프록시 패턴 클래스
public class CalculatorServiceProxy implements Calculator{
private int result;
@Override
public int plus(int left, int right) {
long beforeTime = System.currentTimeMillis();
result = left + right;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long afterTime = System.currentTimeMillis();
long secDiffTime = (afterTime - beforeTime)/1000;
System.out.println(" plus 계산 시간(m) : "+secDiffTime);
return result;
}
@Override
public int subtract(int left, int right) {
long beforeTime = System.currentTimeMillis();
result = left - right;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long afterTime = System.currentTimeMillis();
long secDiffTime = (afterTime - beforeTime)/1000;
System.out.println("subtract 계산 시간(m) : "+secDiffTime);
return result;
}
}
- 프록시 패턴을 사용한 테스트
@SpringBootTest
class CalculatorServiceTest {
@Test
void calculatorServiceProxy() {
Calculator calculatorServiceProxy = new CalculatorServiceProxy();
int left = 10000;
int right = 5000;
int plusResult = calculatorServiceProxy.plus(left, right);
int subtractResult = calculatorServiceProxy.subtract(left, right);
assertEquals(plusResult, left+right);
assertEquals(subtractResult, left-right);
}
}
결과 : plus 계산 시간(m) : 1
subtract 계산 시간(m) : 2
정리
- 프록시 패턴의 의도 : 다른 객체에 접근을 제어하기 위해 대리자를 제공
- 테스트 코드를 보면 생성 객체만 바꿈으로서 Calculator interface는 영향을 받지 않게 Proxy 클래스를 통해서 원하는 목적에 맞게 흐름을 조정할 수 있었다. 해당 패턴은 Spring 프레임워크에 AOP 동작원리에 사용되어지고 있다.
* 중요한 점은 흐름제어만 할 뿐 결과값을 조작하거나 변경시키면 안된다.
참조
- https://readystory.tistory.com/132?category=822867