2. 옵저버 패턴 (Observer Pattern)
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이면 딱 맞는 상황
- 잘 안쓸거 같다. 내가 직접 사용하기 보다 프레임워크를 사용하면서 이 패턴이 적용된 거는 볼 수 있지 않을까 (자바 스윙이라던지..)