프록시 패턴(Proxy Pattern)
필요할 때만 객체를 만들어서 지연된 초기화를 구현할 수 있다. 그러면 객체의 모든 클라이언트들은 어떤 지연된 초기화 코드를 실행해야 하는데, 이것은 아마도 많은 중복 코드를 초래할 것이다.
해결책
프록시 패턴은 원래 서비스 객체와 같은 인터페이스로 새 프록시 클래스를 생성하라고 제안한다. 그러면 프록시 객체를 원래 객체의 모든 클라이언트들에 전달하도록 앱을 전달할 수 있다. 클라이언트로부터 이 요청을 받으면 프록시는 실제 서비스 객체를 생성하고 모든 작업을 이 객체에 위임한다.
클래스의 메인 로직 이전이나 이후에 무언가를 실행해야 하는 경우 프록시는 해당 클래스를 변경하지 않고도 이 무언가를 수행할 수 있도록 한다. 프록시는 원래 클래스와 같은 인터페이스를 구현하므로 실제 서비스 객체를 기대하는 모든 클라이언트에 전달될 수 있다.
구조
- 서비스 인터페이스는 서비스의 인터페이스를 선언한다. 프록시가 서비스 객체로 위장할 수 있으려면 이 인터페이스를 따라야한다
- 서비스는 어떤 유용한 비지니스 로직을 제공한다
- 프록시 클래스에는 서비스 객체를 가리키는 참조 필드가 있다. 요청의 처리를 완료하면 그 후 처리된 요청을 서비스 객체에 전달한다.
- 클라이언트는 같은 인터페이스를 통해 서비스들 및 프로시들과 함께 작동해야한다.
예시
interface ThirdPartyYoutubeLib {
fun listVideos()
fun getVideoInfo(id: String)
fun downloadVideo(id: String)
}
class ThirdPartyYoutube : ThirdPartyYoutube {
override fun listVideos()
override fun getVideoInfo(id: String)
override fun downloadVideo(id: String)
}
class CachedYoutube : ThirdPartyYoutube {
private var service: ThirdPartyYoutubeLib
private var listCache
private var videoCache
val needReset
override fun listVideo() {
if (listCache == null || needReset) {
listCache = service.listVideos()
}
return listCache
}
override fun getVideoInfo(id: String) {
if (videoCache == null || needReset) {
videoCache = service.getVideoInfo(id)
}
return videoCache
}
override fun downloadVideo(id: String) {
if(!downloadExists(id) || needReset) {
service.downloadVideo(id)
}
}
}
class YoutubeManager(val service: ThirdPartyYoutubeLib) {
fun renderVideoPage(id: String) {
service.getVideoInfo(id)
}
fun renderListPanel() {
service.listVideos()
}
fun reactOnUserInput() {
renderVideoPage()
renderListPanel()
}
}
class Application {
fun main() {
val youtubeService = ThirdPartyYoutube()
val youtubeProxy = CachedYoutubeClass(youtubeService)
val manager = YoutubeManager(youtubeProxy)
manager.reactOnUserInput()
}
}
적용
지연된 초기화, 어쩌다 필요한 무거운 객체가 항상 가동되어있어 시스템 자원들을 낭비하는 경우에 사용할 수 있다
특정 클라이언트들만 서비스 객체를 사용할 수 있도록 하려는 경우에 사용할 수 있다.
원격 서비스의 로컬 실행. 서비스 객체가 원격 서버에 있는 경우
요청들의 로깅. 서비스 객체에 대한 요청들의 기록을 유지하려는 경우
요청 결과들의 캐싱. 이것은 클라이언트 요청들의 결과들을 캐시하고 이 캐시들의 수명 주기를 관리해야할 때, 특히 결과들이 상당히 큰 경우에 사용된다.
장단점
클라이언트들이 알지 못하는 상태에서 서비스 객체를 제어할 수 있다
클라이언트들이 신경 쓰지 않을 때 서비스 객체의 수명 주기를 관리할 수 있다
프록시는 서비스 객체가 준비되지 않았거나 사용할 수 없는 경우에도 작동한다
개방 폐쇄 원칙
새로운 클래스를 도입해야 해서 코드가 복잡해질 수 있다
응답이 늦어질수있다