[디자인패턴] 옵저버 패턴 (Observer Pattern)

koline·2023년 9월 8일
0

디자인패턴

목록 보기
20/24

옵저버 패턴


한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에 연락이 가고 자동으로 내용이 갱신되는 방법으로 일대 다의 의존성을 가지며 상호 작용하는 객체 사이에서는 가능하면 느슨하게 결합하는 패턴이다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용되며 Pub/Sub(발행/구독) 모델로도 불린다.

이 패턴을 이해하는데 있어 뉴스 피드나 유튜브로 비유해보면 쉽다. 유튜브 채널은 발행자(Subject)가 되고 구독자들은 관찰자(Observer)가 되는 구조로 보면 된다. 실제로 유튜버가 영상을 올리면 여러명의 구독자들은 모두 영상이 올라왔다는 알림을 받는데, 이를 패턴 구조로 보자면 구독자들은 해당 채널을 구독함으로써 채널에 어떠한 변화(영상을 올리거나 커뮤니티에 글을 쓰거나)가 생기게 되면 바로 연락을 받아 탐지하는 것이다. 반면 구독을 해지하거나 안한 시청자에게는 알림이 가지않게 된다.



구조


  1. Subject : 관찰 대상자를 정의하는 인터페이스
  2. ConcreteSubject : 관찰 당하는 대상자 / 발행자 / 게시자
    Observer들을 리스트(List, Map, Set ..등)로 모아 합성(compositoin)하여 가지고 있음
    Subject의 역할은 관찰자인 Observer들을 내부 리스트에 등록/삭제 하는 인프라를 갖고 있다. (register, remove)
    Subject가 상태를 변경하거나 어떤 동작을 실행할때, Observer 들에게 이벤트 알림(notify)을 발행한다.
  3. Observer : 구독자들을 묶는 인터페이스 (다형성)
  4. ConcreteObserver : 관찰자 / 구독자 / 알림 수신자.
    Observer들은 Subject가 발행한 알림에 대해 현재 상태를 취득한다.
    Subject의 업데이트에 대해 전후 정보를 처리한다.

옵저버 패턴은 여타 다른 디자인 패턴과 똑같이 상호작용할 객체를 합성(compositoin)을 하고 메서드 위임을 통해 구성하는 코드 패턴임은 똑같지만, 핵심은 합성한 객체를 리스트로 관리하고 리스트에 있는 관찰자 객체들에게 모두 메서드 위임을 통한 전파 행위를 한다는 점을 기억하면 된다 (그러므로 구조패턴이 아닌 행위 패턴이다).



구현


// Subject.java (Subject)
public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

// WeatherAPI.java (ConcreteSubject)
public class WeatherAPI implements Subject {
    float temp;
    float humidity;
    float pressure;

    List<Observer> subscribers = new ArrayList<>();

    void measurementsChanged() {
        temp = new Random().nextFloat() * 100;
        humidity = new Random().nextFloat() * 100;
        pressure = new Random().nextFloat() * 100;

        notifyObservers();
    }

    @Override
    public void registerObserver(Observer o) {
        subscribers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        subscribers.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer o : subscribers) {
            o.update(this);
        }
    }
    
}

// Observer.java (Observer)
public interface Observer {
    void update(WeatherAPI api);
}

// WeatherAppUser.java (ConcreteObserver)
public class WeatherAppUser implements Observer {
    private String name;

    WeatherAppUser(String name) {
        this.name = name;
    }

    @Override
    public void update(WeatherAPI api) {
        System.out.printf("%s님이 현재 날씨 상태를 조회함 : %.2f°C %.2fg/m3 %.2fhPa\n", name, api.temp, api.humidity, api.pressure);
    }
}

// Client.java (Client)
public class Client {
    public static void main(String[] args) {
        WeatherAPI api = new WeatherAPI();

        api.registerObserver(new WeatherAppUser("해적왕"));
        api.registerObserver(new WeatherAppUser("손흥민"));
        api.registerObserver(new WeatherAppUser("뉴진스"));

        // 온습도기에서 현재 상태의 온습도 정보가 갱신되고 자동으로 알림 전송
        api.measurementsChanged();

        api.measurementsChanged();
    }
}

// 실행 결과
해적왕님이 현재 날씨 상태를 조회함 : 69.16°C 78.41g/m3 78.56hPa
손흥민님이 현재 날씨 상태를 조회함 : 69.16°C 78.41g/m3 78.56hPa
뉴진스님이 현재 날씨 상태를 조회함 : 69.16°C 78.41g/m3 78.56hPa
해적왕님이 현재 날씨 상태를 조회함 : 33.63°C 39.68g/m3 36.69hPa
손흥민님이 현재 날씨 상태를 조회함 : 33.63°C 39.68g/m3 36.69hPa
뉴진스님이 현재 날씨 상태를 조회함 : 33.63°C 39.68g/m3 36.69hPa

ConcreteSubject는 ConcreteObserver의 목록을 클래스내에 가지고 있으면서 속성에 변화가 생길 경우 notifyObservers 메소드를 사용해 ConcreteObserver의 update함수를 실행하여 알림을 보낸다.


목적

  1. 앱이 한정된 시간, 특정한 케이스에만 다른 객체를 관찰해야 하는 경우
  2. 대상 객체의 상태가 변경될 때마다 다른 객체의 동작을 트리거해야 할때
  3. 한 객체의 상태가 변경되면 다른 객체도 변경해야 할때. 그런데 어떤 객체들이 변경되어야 하는지 몰라도 될 때
  4. MVC 패턴에서도 사용됨 (Model, View, Controller)
  5. MVC의 Model과 View의 관계는 Observer 패턴의 Subject 역할과 Observer 역할의 관계에 대응된다.
  6. 하나의 Model에 복수의 View가 대응한다.

장점

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

단점

  1. 구독자는 알림 순서를 제어할수 없고, 무작위 순서로 알림을 받음
  2. 하드 코딩으로 구현할수는 있겠지만, 복잡성과 결합성만 높아지기 때문에 추천되지는 않는 방법이다.
  3. 옵저버 패턴을 자주 구성하면 구조와 동작을 알아보기 힘들어져 코드 복잡도가 증가한다.
  4. 다수의 옵저버 객체를 등록 이후 해지하지 않는다면 메모리 누수가 발생할 수도 있다.


참고


[디자인패턴] 디자인패턴이란? - 생성패턴, 구조패턴, 행위패턴

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

profile
개발공부를해보자

0개의 댓글