본문 바로가기

설계

[디자인 패턴] 옵저버 패턴 (Observer Pattern) - python 예제 코드

옵저버 패턴은 객체 간의 일대다 의존 관계를 정의하는 디자인 패턴 중 하나입니다. 이 패턴에서, 한 객체가 변경되면 그 객체에 의존하는 다른 객체들에게 알림이 전달되어 자동으로 업데이트 됩니다. 이를 통해 객체 간의 결합도를 줄이고, 유연한 설계를 가능하게 합니다.


1. 구성요소

  • Subject: 변경 사항이 발생하는 객체
  • Observer: 변경 사항을 알림 받는 객체
  • ConcreteSubject: Subject의 구현체
  • ConcreteObserver: Observer의 구현체

 

2. 예제 코드

 

파이썬으로 구현한 예제 코드는 다음과 같습니다.

class Subject:
    def __init__(self):
        self._observers = []

    def attach(self, observer):
        if observer not in self._observers:
            self._observers.append(observer)

    def detach(self, observer):
        try:
            self._observers.remove(observer)
        except ValueError:
            pass

    def notify(self, message):
        for observer in self._observers:
            observer.update(message)


class Observer:
    def update(self, message):
        raise NotImplementedError()


class ConcreteSubject(Subject):
    def __init__(self):
        super().__init__()
        self._state = None

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, value):
        self._state = value
        self.notify(value)


class ConcreteObserver(Observer):
    def __init__(self, name):
        self._name = name

    def update(self, message):
        print(f"{self._name} received message: {message}")


if __name__ == "__main__":
    subject = ConcreteSubject()
    observer1 = ConcreteObserver("Observer 1")
    observer2 = ConcreteObserver("Observer 2")
    subject.attach(observer1)
    subject.attach(observer2)
    subject.state = "new state"


위 코드에서 Subject 클래스는 attach, detach, notify 메서드를 제공합니다. attach 메서드는 옵저버를 추가하는 메서드이며, detach 메서드는 옵저버를 제거하는 메서드입니다. notify 메서드는 Subject의 상태가 변경될 때 호출되며, 등록된 모든 옵저버들에게 상태 변경을 알리는 역할을 합니다.

Observer 클래스는 update 메서드를 제공합니다. 이 메서드는 Subject에서 알림을 받았을 때 호출되며, 구체적인 옵저버들은 이 메서드를 구현하여 자신의 특정 로직을 수행하게 됩니다.

ConcreteSubject 클래스는 Subject를 구현한 구체적인 클래스입니다. 이 클래스는 상태를 저장하고, 상태가 변경될 때마다 notify 메서드를 호출하여 옵저버들에게 알립니다.

ConcreteObserver 클래스는 update 메서드를 구현하여 옵저버가 상태 변경을 수신할 때마다 호출되는 로직을 정의합니다. 위 코드에서는 update 메서드에서 메시지를 출력하는 것으로 구현되어 있습니다.

 

3. 사용 사례

 

옵저버 패턴은 다양한 곳에서 사용됩니다. 예를 들어, GUI 프로그래밍에서는 버튼이나 텍스트 박스 등과 같은 위젯이 Subject 역할을 하고, 이 위젯을 관찰하는 다른 위젯이 Observer 역할을 합니다. 이때, Subject 위젯의 상태가 변경될 때마다 Observer 위젯에게 알림을 전달하여 업데이트를 수행하도록 할 수 있습니다.

또한, 데이터베이스 시스템에서도 옵저버 패턴이 활용됩니다. 예를 들어, 데이터베이스 테이블의 특정 레코드가 변경되었을 때, 이를 관찰하는 다른 프로세스나 서비스에 변경 사항을 알리기 위해 옵저버 패턴을 사용할 수 있습니다.

옵저버 패턴은 객체 간의 결합도를 낮추고, 유연한 설계를 가능하게 해줍니다. 따라서, 객체 지향 프로그래밍에서는 옵저버 패턴을 활용하여 유연하고 확장 가능한 시스템을 구현하는 것이 중요합니다.

 

4. 장점

  • 유연성: 옵저버 패턴은 객체 간의 결합도를 낮추고, 유연한 설계를 가능하게 합니다. Subject 클래스와 Observer 클래스는 서로 독립적이며, 서로의 존재를 알 필요가 없습니다. 이는 시스템의 유연성을 높이고, 변경이 용이하게 만들어줍니다.
  • 확장성: 옵저버 패턴은 새로운 Observer 클래스를 추가하는 것이 매우 쉽습니다. 기존의 Subject 클래스 코드를 수정하지 않고, 새로운 Observer 클래스를 추가하여 Subject 클래스의 동작을 확장할 수 있습니다.
  • 분리된 역할: 옵저버 패턴은 Subject 클래스와 Observer 클래스 각각의 역할을 분리하여 구현할 수 있습니다. 이는 코드의 가독성과 유지보수성을 향상시켜줍니다.

 

5. 단점

  • 성능 문제: 옵저버 패턴을 구현할 때, Subject 클래스에서 notify 메서드를 호출하여 Observer 클래스들에게 상태 변경을 알리기 때문에, 이 과정에서 성능 문제가 발생할 수 있습니다. 만약 옵저버가 많아지거나, 업데이트 로직이 복잡해지면, 성능에 영향을 미칠 수 있습니다.
  • 오버헤드: 옵저버 패턴은 객체 간의 상호작용을 위해 많은 객체를 생성해야 하기 때문에, 메모리와 처리 시간에 오버헤드가 발생할 수 있습니다.
  • 구현의 복잡성: 옵저버 패턴을 구현하는 것은 비교적 어렵습니다. Subject 클래스와 Observer 클래스 사이의 상호작용을 올바르게 구현하기 위해서는 상당한 노력과 경험이 필요할 수 있습니다.