옵저버(Observer) 패턴

2024. 10. 3. 18:50Design Pattern

디자인 패턴: Observer 패턴

Observer 패턴은 객체들 간의 일대다(one-to-many) 관계를 정의하여, 하나의 객체 상태가 변할 때 의존하는 객체들(구독자, observers)이 자동으로 알림을 받고 갱신될 수 있도록 하는 디자인 패턴입니다. 이 패턴은 행위 패턴(Behavioral Patterns) 중 하나로, 객체 사이의 결합도를 줄여서 시스템의 확장성과 유지보수성을 높이는 데 기여합니다.


Observer 패턴의 개념

Observer 패턴은 주체(Subject)와 관찰자(Observer) 간의 관계를 설정하여 주체의 상태가 변할 때 관찰자들에게 통지(알림)하는 방식으로 동작합니다. 이를 통해 주체 객체는 구체적으로 어떤 객체들이 자신을 구독하고 있는지 알 필요 없이,
느슨한 결합(loose coupling)을 유지할 수 있습니다.

주요 구성 요소

  1. Subject (주체)
    • 상태를 관리하고, 상태가 변경되면 Observer에게 알림을 보냅니다.
    • Observer들을 등록하고 제거할 수 있는 메서드를 제공합니다.
  2. Observer (관찰자)
    • Subject의 상태 변화를 감지하고, 그에 따라 동작을 수행합니다.
    • 주체로부터 상태 변경 알림을 받으면, 관련 메서드를 호출하여 갱신합니다.
  3. ConcreteSubject (구체적인 주체)
    • Subject의 구체적인 구현체로, 상태를 관리하고 변경 시 Observer에게 알림을 전송합니다.
  4. ConcreteObserver (구체적인 관찰자)
    • Observer의 구체적인 구현체로, Subject의 상태 변화를 기반으로 특정 행동을 수행합니다.

Observer 패턴 구현 예시

1. Subject와 Observer 인터페이스 정의

// Observer 인터페이스
public interface IObserver
{
    void Update(string message);
}

// Subject 인터페이스
public interface ISubject
{
    void Attach(IObserver observer);  // Observer 등록
    void Detach(IObserver observer);  // Observer 해제
    void Notify();                    // Observer들에게 알림 전송
}

2. 구체적인 주체(ConcreteSubject) 클래스

public class ConcreteSubject : ISubject
{
    private List<IObserver> observers = new List<IObserver>();
    private string message;

    // Observer 등록
    public void Attach(IObserver observer)
    {
        observers.Add(observer);
    }

    // Observer 해제
    public void Detach(IObserver observer)
    {
        observers.Remove(observer);
    }

    // 상태 변화 알림
    public void Notify()
    {
        foreach (var observer in observers)
        {
            observer.Update(message);
        }
    }

    // 주체의 상태 변경
    public void UpdateMessage(string newMessage)
    {
        message = newMessage;
        Notify(); // 상태가 변경되면 알림 전송
    }
}

3. 구체적인 관찰자(ConcreteObserver) 클래스

public class ConcreteObserver : IObserver
{
    private string observerName;

    public ConcreteObserver(string name)
    {
        observerName = name;
    }

    // 알림을 받으면 수행할 작업
    public void Update(string message)
    {
        Console.WriteLine($"{observerName} received message: {message}");
    }
}

4. Observer 패턴 실행 예시

class Program
{
    static void Main(string[] args)
    {
        // 주체 생성
        ConcreteSubject subject = new ConcreteSubject();

        // 관찰자 생성 및 등록
        IObserver observer1 = new ConcreteObserver("Observer 1");
        IObserver observer2 = new ConcreteObserver("Observer 2");

        subject.Attach(observer1);
        subject.Attach(observer2);

        // 주체의 상태 변경
        subject.UpdateMessage("Message 1");
        subject.UpdateMessage("Message 2");

        // Observer 1 해제 후 새로운 상태 업데이트
        subject.Detach(observer1);
        subject.UpdateMessage("Message 3");
    }
}

출력 결과:

Observer 1 received message: Message 1
Observer 2 received message: Message 1
Observer 1 received message: Message 2
Observer 2 received message: Message 2
Observer 2 received message: Message 3

Observer 패턴의 장점

  1. 느슨한 결합 (Loose Coupling)
    • Subject와 Observer 간의 결합도가 낮아, 시스템의 구조를 변경해도 양쪽에 큰 영향을 주지 않습니다.
  2. 확장성
    • 새로운 Observer를 쉽게 추가할 수 있으며, Subject의 기능을 변경하지 않고도 Observer 간의 관계를 확장할 수 있습니다.
  3. 실시간 업데이트
    • 주체의 상태가 변경될 때마다 즉시 Observer에게 알림을 전송하므로, 실시간 상태 업데이트가 가능합니다.

Observer 패턴의 단점

  1. 성능 이슈
    • 많은 Observer가 구독 중일 때, 주체가 상태를 변경할 때마다 모든 Observer에게 알림을 전송해야 하므로 성능에 부담이 될 수 있습니다.
  2. 복잡성 증가
    • 다수의 Observer와 주체 간의 관계를 관리해야 하므로 복잡한 구조를 형성할 수 있습니다.

Observer 패턴의 사용 사례

Observer 패턴은 여러 상황에서 활용됩니다. 대표적인 예시는 다음과 같습니다.

  1. 이벤트 기반 시스템: GUI 애플리케이션에서 버튼 클릭이나 사용자 입력 이벤트를 처리할 때, 이벤트 리스너(Observer)가 상태 변화를 감지하고 작업을 수행합니다.
  2. 실시간 데이터 업데이트: 주식 거래 시스템, 소셜 미디어 알림 등에서 데이터를 실시간으로 업데이트하고 이를 구독자들에게 전송할 때 사용됩니다.
  3. MVC 패턴: Model-View-Controller 패턴에서, 모델의 데이터가 변경되면 뷰에 이를 반영하는 역할로 Observer 패턴을 사용할 수 있습니다.

Observer 패턴은 객체들 간의 느슨한 결합을 유지하면서 상태 변화를 구독자들에게 자동으로 알릴 수 있는 강력한 패턴입니다. 실시간 데이터 업데이트, 이벤트 리스닝 등 다양한 상황에서 활용될 수 있으며, 확장 가능성과 유지보수성 면에서 큰 장점을 제공합니다. 그러나 많은 Observer가 구독할 경우 성능에 영향을 미칠 수 있으므로 적절한 상황에서 활용하는 것이 중요합니다.