일대다 관계에서 요긴하게 쓰이는 패턴으로 하나의 subject와 다수의 observers들이 있다. observer와 subject간 느슨한 연결을 통해 subject의 상태가 바뀌면, subject와 observers간 강한 연결 없이도 subject의 상태가 바뀌었음을 알릴 수 있다.
이런 특징으로 인해, 한 객체의 상태가 바뀌었을때 다른 여러 객체들이 이 변화를 감지해야 할 경우 아주 요긴하게 사용한다. 객체 상태가 빈번하게 바뀌는 유저 인터페이스, 알림 시스템, 이벤트 트리거 시스템에서 쓸 수 있다.
여기서 잠깐, 약한 결합(loosed coupled) 이 뭐지?
일반적으로 이들 사이에 데이터 공유, 참조 없이,메세지만 갖고 통신을 하는 것을 loosely coupled 를 달성한 것으로 간주한다.
Observer 패턴은 생산/구독자 간의 일관된 인터페이스를 통한 메세지 전파를 다루는 디자인 패턴이므로 이는 loosely coupled 를 달성하는 것으로 볼 수 있다
참고: https://m.blog.naver.com/gracefulife/221394894366
class Observable:
def __init__(self):
self.observers = [] # 클래스 init시 옵저버 리스트 생성
def add_observer(self, observer): # 옵저버를 옵저버 리스트에 추가
if observer not in self.observers:
self.observers.append(observer)
def remove_observer(self, observer): # 옵저버를 옵저버 리스트에서 제거
if observer in self.observers:
self.observers.remove(observer)
def notify_observers(self, *args, **kwargs): # 대상의 상태가 바뀔 시 옵저버 객체들에게 알리기
for observer in self.observers:
observer.update(self, *args, **kwargs)
class Observer:
def update(self, observable, *args, **kwargs): # 관찰 대상이 바뀌고 이에 대한 알림이 오면 옵저버 상태를 업데이트함
pass
class WeatherStation(Observable): # 관찰되는 객체
def set_temperature(self, temperature):
self.temperature = temperature
self.notify_observers() # 온도가 바뀌고 옵저버들에게 알림을 줌
class PhoneDisplay(Observer): # 관찰하는 객체
def update(self, observable, *args, **kwargs):
if isinstance(observable, WeatherStation):
temperature = observable.temperature
print(f"Temperature is {temperature} degrees Celsius - Phone")
class ComputerDisplay(Observer): # 관찰하는 객체
def update(self, observable, *args, **kwargs):
if isinstance(observable, WeatherStation):
temperature = observable.temperature
print(f"Temperature is {temperature} degrees Celsius - Computer")
weather_station = WeatherStation()
phone_display = PhoneDisplay()
computer_display = ComputerDisplay()
weather_station.add_observer(phone_display)
weather_station.add_observer(computer_display)
weather_station.set_temperature(25)
결과
Temperature is 25 degrees Celsius - Phone
Temperature is 25 degrees Celsius - Computer
장점:
단점:
객체의 내부 상태가 변할때 객체의 동작(행동)이 바뀌어야 하는 경우 사용한다. 이 패턴에서는 객체의 상태를 각각 고려한다. 각 상태를 나타내는 별도의 클래스가 있고, 이 클래스에서 동작을 수행한다. 클래스의 공통 동작은 부모 클래스에서 추상화할 수 있다.
가령 문이라는 객체가 있고, 이 문이 닫힌 상태일때와 열린 상태일때 동작이 달라진다고 생각해보자. 닫힌 상태에서는 문을 열 수 있고, 열린 상태에서는 문을 닫을 수 있다.
from abc import ABC, abstractmethod
class DoorState(ABC): # State를 나타내는 parent class
@abstractmethod
def open(self):
pass
@abstractmethod
def close(self):
pass
class OpenState(DoorState):
# DoorState를 abstract pattern을 이용해 상속받아 구체화하는 자식 클래스 - 닫힌 상태를 나타냄
def open(self):
print("The door is already open.")
return self
def close(self):
print("Closing the door...")
# Perform door closing process
return ClosedState()
class ClosedState(DoorState):
# DoorState를 abstract pattern을 이용해 상속받아 구체화하는 자식 클래스 - 열린 상태를 나타냄
def open(self):
print("Opening the door...")
# Perform door opening process
return OpenState()
def close(self):
print("The door is already closed.")
return self
class Door:
def __init__(self):
self.current_state = ClosedState() # 초기화때 Close State를 먼저 상속받음 (닫힌 상태로 시작)
def change_state(self, state): # state class를 상속받아 현 객체의 상태를 바꿈
self.current_state = state
def open(self): # current_state를 OpenState로 바꿈
self.change_state(self.current_state.open())
def close(self): # current_state를 CloseState로 바꿈
self.change_state(self.current_state.close())
# Example usage
door = Door()
door.open() #Opening the door...
door.close() #Closing the door...
door.close() # Output: "The door is already closed."
door.open() # Output: "Opening the door..."
door.open() # Output: "The door is already open."
door.close() # Output: "Closing the door..."
위 코드는 DoorState 라는 인터페이스가 있다. 이 인터페이스에서 OpenState와 CloseState라는 두 가지 상태를 나타내는 구체적인 클래스를 만들었다. 그리고 이 상태가 적용될 Door라는 클래스가 있다. 이 클래스는 OpenState 객체 혹은 CloseState 객체를 가지는데, 두 State를 나타내는 클래스 안에 각 상태에서 수행할 함수가 있다.
장점:
단점:
옵저버 패턴
https://www.geeksforgeeks.org/observer-method-python-design-patterns/
https://medium.com/@endlichfelipe/implementing-the-observer-design-pattern-in-python-e1201e32d1f1