본문 바로가기
Programming/OOP

[Design Pattern] Observer Pattern, 옵저버 패턴

by TinKerBellBass 2022. 4. 19.
728x90
반응형

구현보다는 인터페이스에 맞춰서 코딩

AObserver 와 BObserver 는 실체를 가지는 클래스로 구현되었다고 할 수 있다.
구현된 클래스를 직접 사용하는 것, 즉 구현에 맞춰서 코딩하는 것은 무엇이 문제일까?

class Subject {
    private final AObserver aObserver;
    private final BObserver bObserver;
    
    public Subject(AObserver aObserver, BObserver bObserver) {
    	this.aObserver = aObserver;
        this.bObserver = bObserver;
    }

    public void notify(string message) {
        aObserver.update(message);
        bObserver.update(message);
    }
}

class AObserver {...}
class BObserver {...}

이 상황에서 CObserver 를 추가해야 한다고 하면 필드를 선언하고, 생성자에 추가하고, notify 메서드를 수정해야 한다.
특히 생성자의 파라미터를 수정하는 것은 사이드 이펙트가 발생하기 때문에 좋지 않다.

이 문제를 인터페이스에 맞춰서 코딩하는 것으로 해결하면 다음과 같이 된다.

class Subject {
    private List<Observer> observerList;
    
    public Subject() {
    	this.observerList = New ArrayList<>();
    }

    public void notify(string message) {
        for (var observer : ObserverList) {
        	observer.update();
        }
    }
    
    public void regist(Observer observer) {
    	observerList.add(observer);
    }
}

interface Observer {...}
class AObserver implements Observer {...}
class BObserver implements Observer {...}

CObserver 를 추가한다고 하면 CObserver 객체가 regist 메서드를 호출해서 등록만 하면 되기 때문에 변경이 발생하지 않는다.
유지보수가 편하기 위해서는 변경에 강해야 한다.

Observer Pattern

옵저버 패턴은 하나의 이벤트가 발생했을 때 처리해야 하는 작업이 다수일 때 사용할 수 있는 패턴이다.
이벤트를 발생시키는 객체가 다수의 객체에게 이벤트가 발생했으니 처리하라고 알리는 것이다.

만약 옵저버 패턴을 사용하지 않는다고 하면 이벤트가 발생했을 때 다수의 구현 객체에게 하나하나 메시지를 보내야 한다.
이벤트를 발생 시키는 객체는 구현 객체들을 주입받아야 하기 때문에 의존 관계가 형성되고 유연하지 못한 코드가 된다.
즉 구현에 맞춰서 코딩하는 단점들을 그대로 다 가지고 가게 된다.

Observer Pattern Class Diagram

정보가 갱신되었거나 이벤트가 발생했을 때 정보나 이벤트의 출판은 Subject 인터페이스에 정의된 메서드에 의해 이루어진다.
옵저버를가 구독을 신청하는 subscribe 메서드, 구독을 취소하는 unsubscribe 메서드, 출판을 위한 publish 메서드가 정의되어 있다.
Subject 인터페이스를 구현한 Messenger 는 메시지를 입력받아 구독한 옵저버들에게 메시지를 알리고
메시지가 도착한 옵저버들은 각각의 로직을 처리한다.
MailSender 는 메일을 발송하고 AppAlarmSender 는 앱 알람을 발송한다.

전체 소스는 https://github.com/Jongwon-Hyun/design_pattern

Observer Pattern VS. PubSub Pattern

옵저버 패턴과 출판구독 패턴은 거의 똑같다고 해도 무방한데 차이점은 중간에 브로커가 있느냐 아니냐 이다.
옵저버 패턴에서 출판을 담당하는 객체는 옵저버의 추가, 삭제 등의 메서드를 통해 옵저버들을 직접 관리하지만,
출판구독 패턴에서는 출판을 담당하는 객체가 구독하는 객체들의 존재를 알지 못한다.
정보, 이벤트 등의 출판은 중간에 존재하는 메시지큐 등의 브로커에게 이루어지고,
구독은 중간에 존재하는 메시지큐 등의 브로커로부터 정보, 이벤트 등을 얻어온다.

옵저버 패턴을 활용한 라이브러리는 대표적으로 rxjs 가 있다.
공식 홈에서 가져온 코드인데,
fromEvent(doument, 'click') 가 출판을 담당하는 Subject ,
파이프는 중간 연산으로서 출판된 정보, 이벤트를 가공하거나 조작하고,
subscribe 파라미터에 들어가는 람다는 옵저버로, subscribe 메서드를 통해 구독을 신청한다.
앵귤러 할 때가 생각나네... 프론트엔드 다 좋은데 CSS 가 너무 말을 안 들어서... 네.. 제 실력 부족...

import { fromEvent, throttleTime, scan } from 'rxjs';

fromEvent(document, 'click')
  .pipe(
    throttleTime(1000),
    scan((count) => count + 1, 0)
  )
  .subscribe((count) => console.log(`Clicked ${count} times`));

출판구독 패턴의 대표적인 예는 마이크로서비스에서 서비스 간 메시지 통신을 위한 카프카이다.
출판구독 패턴의 최대 장점은 출판과 구독이 서로 모른다는 것인데,
의존성이 없기 때문에 서로 다른 모듈 간의 출판구독도 가능한 것이다.
조금 더 실제적인 예를 들어보면,
은행계좌 신청 모듈에서 개설 요청을 받아서 데이터베이스에 요청 정보를 저장하고,
은행계좌 개설 모듈에서는 데이터베이스에서 요청 정보를 가져와서 계좌 개설을 처리하는 시스템에서,
출판은 신청 모듈이고 구독은 개설 모듈이고 브로커는 데이터베이스가 된다.

728x90
반응형

댓글