티스토리 뷰

1. 옵저버 패턴이란?

한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 1:N 의존성을 정의한다.

 

구성요소

1. Subject

- Observer 목록을 가지고 있다. Observer의 수에는 제한이 없음.

- Observer를 추가/제거 하는 인터페이스 제공

 

2. Observer

- 업데이트 인터페이스를 제공. (이 업데이트 인터페이스는 해당되는 Subject에 변화가 있을 때 갱신하는 것을 의미)

 

3. ConcreteSubject

- ConcreteObserver 객체들이 필요로 하는 정보를 가지고 있다.

- 상태가 변하면 가지고 있는 ConcreteObserver들에게 공지함.

 

4. ConcreteObserver

- ConcreteSubject 객체에 대해 참조

- 해당되는 Subject와 동기화 되는 상태를 가지고 있다.

- Observer의 업데이트 인터페이스를 구현함.

 

기본이 되는 디자인 원칙

Loose Coupling: 서로 상호작용을 하지만 서로에 대해 잘 모르는 것을 의미

- Subject가 Observer에 대해 아는 것은 Observer가 특정 인터페이스를 구현한다는 것 뿐이다.

- Observer는 언제든지 새로 추가/제거

- 새로운 형식의 Observer를 추가하려고 할 때 Subject에 변경 필요 없음

- Subject / Observer는 서로 독립적으로 재사용 가능

- Subject / Observer가 바뀌더라도 서로 영향 없음

 

2. 동기

객체의 변화가 synchronize 되야하거나 broadcast의 성격을 띌 때 사용할 수 있다.

 

장점:

- 상태를 변경하는 객체(publisher)와 변경을 감지하는 객체(subscriber) 관계를 느슨하게 유지

- subject의 상태 변경을 주기적으로 조회하지 않고 자동으로 감지

- 런타임에 옵저버를 추가/제거

 

단점:

- 복잡도 증가

- 다수의 Observer 객체를 등록 이후 해지하지 않으면 memory leak

-> 해지하는게 가장 좋지만 Weak Reference도 참고

- 원치않은 notification

- 서로에 대한 정보가 없다보니 update 비용을 모른다

 

3. 예제

public interface Subject { 
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers(); // subject 객체의 상태 변경 시  모든 객체들에게 공지
}

public interface Observer {
    public void update(Object state);
}

public interface DisplayObject {
    public void display();
}
public class ConcreteSubject implements Subject {
    private ArrayList observers;
    private Object state;

    public ConcreteSubject() {
        observers = new ArrayList();
    }

    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if ( i > = 0) {
            observers.remove(i);
        }
    }

    public void notifyObservers() {
        for (Observer o : observers) {
            o.update(state);
        }
    }

    public void stateChanged() { 
        notifyObservers();
    }

    public void setState(Object newState) {
        this.state = newState;
        stateChanged();
    }
}
public class ConcreteObserver implements Observer, DisplayObject {
    private Subject subject;
    private Object state;

    public ConcreteObserver(Subject subject) {
        this.subject = subject;
        subject.registerObserver(this);
    }

    public void update(Object state) {
        this.state = state;
        display();
    }

    public void display() {
        System.out.println("Current State:" + state.toString());
    }
}

Java에서의 옵저버 패턴

-> java8 이후의 대안 (Flow API)

-> Spring에서는 ApplicationRunner, ApplicationEventPublisher, ApplicationContext 사용

 

개인적인 포인트

- 두 객체간 의존성이 포착되고 그게 추상적으로 일방향적이어야 된다는 느낌이 들 때 떠올려볼만한 패턴인 것 같다.

- 특히 그 일방향성을 가진 의존성이 1:N이면 딱 맞는 상황

- 잘 안쓸거 같다. 내가 직접 사용하기 보다 프레임워크를 사용하면서 이 패턴이 적용된 거는 볼 수 있지 않을까 (자바 스윙이라던지..)