옵저버 패턴(Observer Pattern) ; 의존, 일대다관계(단방향), 상태, 느슨한 결합

한 객체의 상태가 바뀌면, 그 객체에 의존하는 다른 객체들한테 연락이 가고,
자동으로 내용이 갱신되는 방식으로 일대다(One-to-Many) 의존성을 정의한다.

-> 출판사와 구독자 의 관계와 정확하게 부합한다.

느슨한 결합(Loose Coupling)

두 객체가 느슨하게 결합되어 있다는 것은, 그 둘이 상호작용을 하긴 하지만 서로에 대해 잘 모른다는 것을 의미한다.

옵저버 패턴에서는 주제와 옵저버가 느슨하게 결합되어 있는 객체 디자인을 제공한다. 왜 그럴까?

객체 사이의 상호의존성을 최소화 할 수 있기 때문이다.

  1. 옵저버는 언제든지 새로 추가하고 제거할 수 있다.
  2. 새로운 형식의 옵저버를 추가할때도 주제를 전혀 변경할 필요가 없다.
  3. 주제와 옵저버는 서로 독립적으로 재사용할 수 있다.
  4. 주제나 옵저버가 바뀌더라도 서로한테 영향을 미치는 정도가 낮다.

느슨하게 결합하는 디자인을 사용하면 변경 사항이 생겨도 무난히 처리할 수 있는 유연한 객체지향 시스템을 구축할 수 있다.

책에서 나오는 예시

책에서는 기상데이터(주제)와 이를 표시하는 서드파티디스플레이 모듈(옵저버)의 예시를 들어 설명하고 있다.
기상 데이터보다는 개인적으로 발행 - 구독이란 관계에서 블로그의 글 게시 - 구독자 관계를 구현해보았다.

Blog

1
2
3
4
5
6
7
8
import java.util.Observable;

public class Blog extends Observable {
public void addBlogPost(String blogPostTitle) {
setChanged();
notifyObservers(blogPostTitle);
}
}

Subscriber

1
2
3
4
5
6
7
8
9
import java.util.Observable;
import java.util.Observer;

public class Subscriber implements Observer {
@Override
public void update(Observable blog, Object blogPostTitle) {
System.out.println(getClass().getSimpleName() + " 수신 ||||| " + blogPostTitle);
}
}

Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class BlogTest {

@Test
void observerTest() throws Exception {
Blog myBlog = new Blog();
Observer subscriber = new Subscriber();
Observer rssSubscriber = new RssSubscriber();
Observer mailSubscriber = new MailSubscriber();

// Register the observer with the subject.
myBlog.addObserver(subscriber);
myBlog.addObserver(rssSubscriber);
myBlog.addObserver(mailSubscriber);

// Add some blog post titles.
myBlog.addBlogPost("Docker in 5 Steps");
myBlog.addBlogPost("Angular vs React");
}
}

/** 결과
MailSubscriber 수신 ||||| Docker in 5 Steps
RssSubscriber 수신 ||||| Docker in 5 Steps
Subscriber 수신 ||||| Docker in 5 Steps
MailSubscriber 수신 ||||| Angular vs React
RssSubscriber 수신 ||||| Angular vs React
Subscriber 수신 ||||| Angular vs React
*/

Pull vs Push (Observer vs Polling)

Pull vs Push(행위 관점)

공통점 : 데이터가 전이된다.

차이점

  • pull : 데이터를 서버로부터 끌어오는 행위
  • push : 서버가 데이터를 클라이언트에게 밀어주는 행위

Polling vs Observer(인터페이스 관점)

공통점 : 서로 규정된 인터페이스가 있다.

차이점 :

  • polling : 데이터가 필요한 주체가 제공 주체에게 묻는 구조
  • observer : 제공 주체의 상태가 바뀌면, 필요 주체에게 데이터를 자동으로 전이하는 구조

Spring 에서 찾은 옵저버 패턴

Spring 에서는 고전적인 옵저버 패턴의 notify - update 구조보다는
발행 - 구독 관계가 더욱 많다.

대표적인 예로 ExceptionListener - ExceptionHandler가 있다.
*Listener - *Handler 모두 공통이긴하지만, 어떤 이벤트에 대하여 발생여부를 감지 하고,
이를 핸들링하는 부분 까지 모두 옵저버 패턴의 일환이라고 볼 수 있다.

생각해보자

모바일 환경으로 들어오면서 옵저버 패턴은 흔히 푸쉬 알림이라는 기술의 근간이 되었다고 생각한다.
이런 환경이 더욱 더 개선해서 메시지 매커니즘이나, 리액티브 프로그래밍, Webflux 등등 여러 기술의 근간이 된 것으로 생각된다.

옵저버 패턴의 가장 강력한 구조는 특정 객체의 상태 변경에 대하여, 자동으로 전파하는 것이 옵저버 패턴의 핵심이라고 생각된다.
그렇다면, ‘왜 특정 객체의 상태 변경에 대해 자동으로 알림받기를 원하는가?’ 생각해 봐야하는데,
고전적인 폴링(Polling) 방식에서는

  • 상태 변경이 빈번하지 않은데 계속 통신해야하는 오버헤드
  • 특히 빈번 주기와 상태 변경의 빈도에서 동기화를 위해 주기를 적절히 조절해야 한다는 점
    에서 폴링이란 방식의 장점보다는 단점이 더욱 더 명확했다.

특히 상태 변경 자동 전파 라는 개념이, 이벤트라는 비 주기적 특성과도 잘 맞는 것 같아서
웹플러스 등으로 발전하지 않았나 생각이 든다.

리액티브 프로그래밍, 웹플럭스 등도 학습대상으로 두고 있었지만 이번 기회에 간략하게나마 그 기술의 탄생이유를 엿볼 수 있었다.

참고자료