디자인 패턴 중 Proxy 패턴에 대해 설명합니다.
설명
타겟 객체에 대한 접근을 제어하는 대리인에 해당하는 객체를 중간에 추가하는 패턴입니다. 타겟 객체에 전달되는 요청 전/후 처리를 하고 실질적인 작업은 타겟 객체에 위임합니다. 타겟 객체와 동일한 인터페이스로 클라이언트에 제공되기 때문에 클라이언트는 타겟 객체인지 프록시 객체인지 알 필요가 없습니다.
- ServiceInterface: Proxy와 Service(타겟)의 공통 인터페이스를 정의합니다. 클라이언트는 해당 인터페이스에 의존하여 Proxy인지 Service인지 알 필요 없습니다.
- Proxy: Service(타겟) 참조를 필드에 할당하여 실질적인 로직은 Service에게 위임하고 그 전/후 처리를 수행하여 클라이언트에게 결과를 전달합니다. 보통 Service의 라이프사이클을 생성/관리합니다.
- Service: 실질적인 로직을 수행하는 클래스입니다.
- Client: Proxy든 Service든 상관할 필요없이 ServiceInteface에 의존하여 동일하게 취급합니다.
다음과 같은 요구가 있을 때 유용합니다.
- Lazy Initialization: 객체 생성시 데이터베이스 접근, 외부 서버와 통신 등과 같은 오래 걸릴 수 있는 비용이 비싼 작업을 포함하고 있다면 실제로 필요할 때 할 수 있도록 지연합니다.
- Access control: 객체 접근을 제어해야 하는 경우입니다. 자격이 있는 클라이언트에만 타겟 객체 접근을 허용합니다.
- Remote server: 원격 서버에게 요청을 전달해야 하는 경우입니다. 원격 서버에게 요청을 전달하기 위한 전/후 처리가 필요합니다.
- Caching: 반복적으로 발생하는 요청의 결과를 저장했다가 같은 요청이 들어오면 미리 저장한 결과를 반환합니다.
- Logging: 실질적은 로직 수행 전/후 과정에 기록을 남깁니다.
적용 예시
클라이언트에게 이미지를 요청받으면 원격 서버에서 이미지를 다운로드하여 전달하는 로직이 있다고 가정합니다. 기존에 요청했던 이미지를 또 요청하는 경우 캐싱 데이터를 반환할 수 있도록 코드를 수정하고자 할 때 Proxy 패턴을 사용할 수 있습니다.
from abc import ABC, abstractmethod
from typing import Dict
class ImageServiceInterface(ABC):
@abstractmethod
def get_image(id: str) -> bytes: ...
class ImageService(ImageServiceInterface):
def get_image(id: str) -> bytes:
# download from remote server
...
class ImageServiceProxy(ImageServiceInterface):
def __init__(self) -> None:
self._image_service = ImageService()
self.cache: Dict[str, bytes] = {}
def get_image(self, id: str) -> bytes:
if id in self.cache:
return self.cache[id]
image = self._image_service.get_image(id)
self.cache[id] = image
return image
image_service = ImageServiceProxy()
image_service.get_image("image_1")
ImageServiceProxy를 통해 ImageService로부터 얻은 이미지를 캐싱하고 동일한 이미지 요청 시 캐싱된 이미지를 반환합니다.
참조
'객체 지향 > 디자인 패턴' 카테고리의 다른 글
디자인 패턴 - Flyweight (0) | 2023.10.28 |
---|---|
디자인 패턴 - Facade (0) | 2023.10.28 |
디자인 패턴 - Decorator (1) | 2023.10.25 |
디자인 패턴 - Composite (0) | 2023.10.22 |
디자인 패턴 - Bridge (0) | 2023.10.21 |