티스토리 뷰

옵서버 패턴이란?

옵저버 패턴(Observer Pattern)은 1대 다의 의존관계이며 객체 상태의 변화가 다른 의존 객체에 통지되고 자동으로 업데이트 되게 합니다. Pub/Sub(발행/구독) 모델로도 알려져 있기도 합니다. Java에서는 Observer, Observable를 지원하기도 합니다.

프로그래밍적으로 옵저버 패턴은 사실 '관찰' 하기 보단 갱신을 위한 힌트 정보를 '전달' 받길 기다린다고 보는 것이 적절하다. 관찰자라는 단어 뉘앙스에서 능동적으로 대상을 관찰하는 것처럼 느껴지지만, 사실 대상 객체로부터 수동적으로 전달 받기를 기다리고 있기 때문이다. 즉,관찰자(Observer)는 발행자(Subject)가 알림을 주기를 항시 대기하고 있는 것이다.

옵서버 패턴 사용 방법

발행자 클래스

  • ISubject : 발행자를 정의하는 인터페이스
  • ConcreteSubject 
    • Observer들을 리스트(List, Map, Set ..등)로 모아 합성(compositoin)하여 가지고 있음
    • Subject의 역할은 관찰자인 Observer들을 내부 리스트에 등록/삭제 하는 인프라를 갖고 있다.
    • Subject가 상태를 변경하거나 어떤 동작을 실행할때, Observer 들에게 이벤트 알림(notify)을 발행한다.

구독자 클래스

  • IObserver : 구독자들을 묶는 인터페이스 (다형성)
  • Observer : 관찰자 / 구독자 / 알림 수신자. (알림을 기다리고 있는 대상)
    • Observer들은 Subject가 발행한 알림에 대해 현재 상태를 취득한다.
    • Subject의 업데이트에 대해 전후 정보를 처리한다

 

옵서버 사용 절차

STEP 1. 모든 옵서버가 같은 타입이 되도록 선언해 옵서버를 같은 방법으로 동작하게 만든다.

STEP 2. 옵서버를 등록시킨다.

  •  Subject에 다음 두 가지 메서드를 추가한다.
    • registerObserver : 옵서버를 리스트에 추가
    • removeObserver : 옵서버를 리스트에서 삭제

STEP 3. 이벤트가 발생해 Subject의 상태가 바뀌면 옵서버에게 통보

  • Subject는 notify 메서드를 구현
    • 옵서버의 리스틀 따라 순회하면서 각 옵서버에게 update메서드 호출

STEP 4. Subject로부터 정보를 받아온다.

클래스 구조

// 관찰 대상자 / 발행자
interface ISubject {
    void registerObserver(IObserver o);
    void removeObserver(IObserver o);
    void notifyObserver();
}

class ConcreteSubject implements ISubject {
    // 관찰자들을 등록하여 담는 리스트
    List<IObserver> observers = new ArrayList<>();
    private String subjectState;

    // 관찰자를 리스트에 등록
    @Override
    public void registerObserver(IObserver o) {
        observers.add(o);
        System.out.println(o + " 구독 완료");
    }

    // 관찰자를 리스트에 제거
    @Override
    public void removeObserver(IObserver o) {
        observers.remove(o);
        System.out.println(o + " 구독 취소");
    }

    // 관찰자에게 이벤트 송신
    @Override
    public void notifyObserver() {
        for(IObserver o : observers) { // 관찰자 리스트를 순회하며
            o.update(); // 위임
        }
    }
    
    public void setState(String state){
    	this.subjectState=state;
        notifyObserver();
     }
     
     public String getState(){
     	return subjectState;
     }
}
// 관찰자 / 구독자
interface IObserver {
    void update();
}

class ConcreteObserver implements IObserver {

    private ConcreteSubject concreteSubject;
    
    @Override
    public void update() {
        System.out.println("ConcreteObserver 한테 이벤트 알림이 왔습니다.");
        concreteSubject.getState();
    }

    public String toString() { return "ConcreteObserver"; }
}
public class Client {
    public static void main(String[] args) {

        // 발행자 등록
        ISubject publisher = new ConcreteSubject();

        // 발행자를 구독할 관찰자들 리스트로 등록
        IObserver o1 = new ConcreteObserver();
        IObserver o2 = new ConcreteObserver();
        publisher.registerObserver(o1);
        publisher.registerObserver(o2);
        o1.setSubject(publisher);
        o2.setSubject(publisher);

        // 관찰자에게 이벤트 전파
        publisher.notifyObserver();
        
        // 상태 변경
        publisher.setState("수정");
        
    }
}

 


패턴 사용 시기

  • 앱이 한정된 시간, 특정한 케이스에만 다른 객체를 관찰해야 하는 경우
  • 대상 객체의 상태가 변경될 때마다 다른 객체의 동작을 트리거해야 할때
  • 한 객체의 상태가 변경되면 다른 객체도 변경해야 할때. 그런데 어떤 객체들이 변경되어야 하는지 몰라도 될 때

패턴 장점

  • Subject의 상태 변경을 주기적으로 조회하지 않고 자동으로 감지할 수 있습니다.
  • 발행자의 코드를 변경하지 않고도 새 구독자 클래스를 도입할 수 있어 개방 폐쇄 원칙(OCP) 준수합니다.
  • 런타임 시점에서에 발행자와 구독 알림 관계를 맺을 수 있습니다.
  • 상태를 변경하는 객체(Subject)와 변경을 감지하는 객체(Observer)의 관계를 느슨하게 유지할 수 있습니다. (느슨한 결합)

패턴 단점

  • 구독자는 알림 순서를 제어할수 없고, 무작위 순서로 알림을 받습니다.
  • 옵저버 패턴을 자주 구성하면 구조와 동작을 알아보기 힘들어져 코드 복잡도가 증가합니다.
  • 다수의 옵저버 객체를 등록 이후 해지하지 않는다면 메모리 누수가 발생할 수도 있습니다.

참고

 

💠 옵저버(Observer) 패턴 - 완벽 마스터하기

Observer Pattern 옵저버 패턴(Observer Pattern)은 옵저버(관찰자)들이 관찰하고 있는 대상자의 상태가 변화가 있을 때마다 대상자는 직접 목록의 각 관찰자들에게 통지하고, 관찰자들은 알림을 받아 조

inpa.tistory.com

 

'CS > Design Pattern' 카테고리의 다른 글

프록시 패턴 (Proxy Pattern)  (0) 2024.06.03
중재자 패턴 (Mediator Pattern)  (0) 2024.04.18
싱글톤 패턴 (Singleton Pattern)  (0) 2024.04.18
브리지 패턴 (Bridge Pattern)  (0) 2024.04.17
컴포지트 패턴 (Composite Pattern)  (0) 2024.04.17