Exist Technote
[Python] Observer Pattern (옵저버 패턴) 본문
Observer Pattern이란?
- 감시자 패턴.
- 객체들 사이에 일대 다의 의존 관계를 정의하여, 일 객체의 상태가 변할 때 다 객체들이 변화를 통지받아 동작을 수행할 수 있게 만드는 패턴.
- 구독-발행 패턴과 유사점이 많다.
예제
from contextlib import suppress
# 옵저버들을 관리하는 객체 subject. 확장할 수 있도록 설계된 구조가 멋지다.
class Subject:
# 옵저버를 추가 or 제거
def __init__(self) -> None:
self._observers = []
def attach(self, observer) -> None:
if observer not in self._observers:
self._observers.append(observer)
def detach(self, observer) -> None:
# contextlib.suppress()
# 인자로 전달된 예외가 발생할 경우 예외를 무시하고 with 블럭을 빠져나간다.
with suppress(ValueError):
self._observers.remove(observer)
# 옵저버들의 update 메소드를 일괄적으로 실행
def notify(self, modifier = None) -> None:
for observer in self._observers:
if modifier != observer: # 타겟 옵저버 필터링 조건.
observer.update(self)
# Subject를 확장하여 활용하는 data
class Data(Subject):
def __init__(self, name: str = "") -> None:
super().__init__()
self.name = name
self._data = 0
@property
def data(self) -> int:
return self._data
# 프로퍼티를 이렇게 활용할 수 있다니. 멋진데?
@data.setter
def data(self, value: int) -> None:
self._data = value
self.notify()
# 옵저버의 인터페이스를 정의하는 클래스 Observer
class Observer:
def update(self, subject) -> None:
pass
# 구현된 옵저버 클래스들. Observer 인터페이스를 따른다면 얼마든지 추가될 수 있다.
class HexViewer:
def update(self, subject: Data) -> None:
print(f"HexViewer: Subject {subject.name} has data 0x{subject.data:x}")
class DecimalViewer:
def update(self, subject: Data) -> None:
print(f"DecimalViewer: Subject {subject.name} has data {subject.data}")
if __name__ == "__main__":
# 옵저버 관리자
data1 = Data('Data 1')
data2 = Data('Data 2')
# 옵저버
view1 = DecimalViewer()
view2 = HexViewer()
# 옵저버 관리자에게 옵저버 할당
data1.attach(view1)
data1.attach(view2)
data2.attach(view2)
data2.attach(view1)
# 동작
data1.data = 10
# DecimalViewer: Subject Data 1 has data 10
# HexViewer: Subject Data 1 has data 0xa
data2.data = 15
# HexViewer: Subject Data 2 has data 0xf
# DecimalViewer: Subject Data 2 has data 15
# 옵저버 관리자에 등록되어 있는 옵저버 제거
data1.detach(view2)
data2.detach(view2)
# 동작
data1.data = 10
data2.data = 15
# DecimalViewer: Subject Data 1 has data 10
# DecimalViewer: Subject Data 2 has data 15
여담
- Subject의 메소드 notify의 modifier 매개변수를 어떻게 활용할 수 있으려나.
- 크롬의 다크모드를 예로 들을 수 있겠다.
- 클라이언트가 '다크모드'를 선택했다.
- '다크모드'의 옵저버들이 일괄적으로 적절한 스킨으로 선택한다.
- 발행-구독 패턴과 유사점이 많다.
- subject가 observer를 보유하는 것이 아닌, 어떤 중간 객체(일반적으로 Queue)를 두어 subject와 observer가 중간 객체를 통해 소통하는 패턴이라고 볼 수 있다.
- subject와 observer가 서로를 인지할 필요가 없어 결합도가 낮아진다.
- observer는 필요할 때에만 중간 객체와 소통하여 subject의 변화를 감지하면 된다.
- 크로스 도메인 상황에서도 구현이 가능해진다.
- 결론적으로...
- 특정 객체의 상태 변화 또는 동작이 여러 객체들의 동작 또는 변경을 필요로 할 때 사용될 수 있는 패턴.
- 리스트와 파이썬 for문 간의 관계를 패턴으로 만든 것이 옵저버 패턴이 아닐까.
'Design Pattern > Behavioral' 카테고리의 다른 글
[Python] Template Method Pattern (템플릿 메소드) (0) | 2020.10.18 |
---|---|
[Python] State Pattern (상태 패턴) (1) | 2020.10.16 |
[Python] Registry Pattern (레지스트리 패턴) (0) | 2020.10.15 |
[Python] Chaining Method Pattern (체이닝 메소드 패턴) (0) | 2020.10.14 |
Comments