1. 옵저버 패턴 (Observer Pattern)
옵저버(Observer)를 직역하면 관찰자, 감시자를 뜻한다.
옵저버 패턴(Observer Pattern)은 옵저버(감시자)가 관찰하는 대상자의 상태 변화(이벤트)가 발생되는 것을 감시하는 패턴이다.
옵저버 패턴은 다른 디자인 패턴과 다르게 일대다(1:N) 의존성을 가진다.
옵저버 패턴을 유튜브 채널과 구독에 비유하면 이해하기가 좀 더 쉽다.
유튜브 채널을 구독하는 구독자는 자신이 구독한 채널의 새 영상이 업로드되면 알림을 받게 된다. 여기서 유튜브 채널은 관찰 대상자가 되고, 구독자는 옵저버(관찰자)가 된다.
유튜브 채널에 새 영상이 업로드되는 이벤트를 감지하고, 미리 정의해 둔 알림을 보내는 동작이 수행된다.
이처럼 옵저버 패턴은 이벤트가 발생될 때마다 미리 정의해 둔 동작을 수행하도록 해주는 디자인 패턴이다.
2. 옵저버 패턴의 장단점
[ 장점 ]
- 실시간으로 객체의 상태 변화를 알 수 있다.
- 옵저버와 관찰 대상자(Subject)의 관계를 느슨하게 유지할 수 있다. (느슨한 결합)
- 느슨한 결합은 두 객체가 상호작용을 하지만 서로에 대해 잘 모른다는 의미이다.
- 관찰 대상자(Subject)는 옵저버가 무엇을 하는지, 구상 클래스가 무엇인지 알 필요가 없다.
- 옵저버는 언제든지 추가, 삭제가 가능하며, 옵저버와 관찰 대상자(Subject)는 서로 영향을 주고 받지 않게 된다.
[ 단점 ]
- 옵저버 패턴을 많이 사용하게 되면 상태 관리가 어려워진다.
- 사용되지 않는 옵저버를 삭제하지 않으면 메모리 누수가 발생할 수 있다.
3. Spring의 옵저버 패턴 활용 방법
Spring에서는 옵저버 패턴을 어떻게 사용할까?
가장 대표적으로, Spring의 이벤트 발행을 담당하는 ApplicationEventPublisher 인터페이스가 있다.
@EventListener 어노테이션을 사용해서 이벤트를 정의하고, ApplicationEventPublisher 인터페이스를 사용해서 이벤트를 발행한다.
이 때, @EventListener 가 옵저버(관찰자)가 되고, ApplicationEventPublisher 인터페이스가 관찰 대상자(Subject)가 된다.
Spring 이벤트 발행과 구독에 관련해서 정말 잘 정리해주신 망나니개발자님의 포스팅을 참고하면 좋을 것 같다...!
4. Java로 옵저버 패턴 구현하기
Java에서는 JDK에서 지원하는 내장 옵저버 패턴이 존재한다.
본 포스팅에서는 내장 옵저버 패턴을 사용하지 않고 직접 간단하게 구현해 볼 예정이다.
위에서 예시로 들었던 유튜브 채널과 구독자를 구현해보려고 한다.
Observer(관찰자) : 구독자
Subject(관찰 대상자) : 유튜브 채널
이벤트 : 채널 커뮤니티에 새로운 글이 업로드되면 구독자들은 알림을 받는다.
Channel
관찰 대상자인 Subject 인터페이스이다.
- addObserver : 관찰 대상자를 구독할 옵저버를 추가하는 기능
- deleteObserver : 관찰 대상자 구독 취소하는 기능
- notifyObserver : 옵저버에게 알림을 전송하는 기능
public interface Channel {
void addObserver(Subscriber subscriber); // 옵저버 추가
void deleteObserver(Subscriber subscriber); // 옵저버 삭제
void notifyObserver(String message); // 옵저버에게 알림 전송
}
Subscriber
관찰자인 Observer 인터페이스이다.
- update : 구독 중인 관찰 대상자의 알림을 받았는지 확인하는 기능
public interface Subscriber {
void update(String message);
}
CatChannel
Channel 인터페이스를 정의하는 CatChannel 클래스이다.
- subscriberList : 해당 채널을 구독하는 Observer 리스트
- uploadPost : 해당 채널의 이벤트 행위
public class CatChannel implements Channel {
private List<Subscriber> subscriberList = new ArrayList<>();
public void uploadPost() {
System.out.println("이벤트 발생 : 채널 커뮤니티 글 업로드");
notifyObserver("고양이 채널 커뮤니티에 새 글이 업로드 되었습니다.");
}
@Override
public void addObserver(Subscriber subscriber) {
subscriberList.add(subscriber);
}
@Override
public void deleteObserver(Subscriber subscriber) {
subscriberList.remove(subscriber);
}
@Override
public void notifyObserver(String message) {
subscriberList.forEach(subscriber -> subscriber.update(message));
}
}
ASubscriber
Subscriber 인터페이스를 정의하는 ASubscriber 클래스이다.
public class ASubscriber implements Subscriber {
@Override
public void update(String message) {
System.out.println("A 구독자 수신 완료 : " + message);
}
}
BSubscriber
Subscriber 인터페이스를 정의하는 BSubscriber 클래스이다.
public class BSubscriber implements Subscriber {
@Override
public void update(String message) {
System.out.println("B 구독자 수신 완료 : " + message);
}
}
CSubscriber
Subscriber 인터페이스를 정의하는 CSubscriber 클래스이다.
public class CSubscriber implements Subscriber {
@Override
public void update(String message) {
System.out.println("C 구독자 수신 완료 : " + message);
}
}
ObserverMain
옵저버 패턴을 테스트할 Main 클래스이다.
CatChannel에 a, b, c 옵저버를 모두 등록한 후의 결과와 b 옵저버를 삭제한 후의 결과를 확인해 보자.
public class ObserverMain {
public static void main(String[] args) {
CatChannel catChannel = new CatChannel();
Subscriber aSubscriber = new ASubscriber();
Subscriber bSubscriber = new BSubscriber();
Subscriber cSubscriber = new CSubscriber();
catChannel.addObserver(aSubscriber);
catChannel.addObserver(bSubscriber);
catChannel.addObserver(cSubscriber);
catChannel.uploadPost();
System.out.println("============");
catChannel.deleteObserver(bSubscriber);
catChannel.uploadPost();
}
}
실행 결과
Subject의 이벤트가 발생했을 때, 구독 중인 Observer 에게만 알림이 가는 것을 확인할 수 있다.
출처
긴 글 읽어주셔서 감사합니다 🍀
잘못 작성된 내용은 피드백 주시면 반영하겠습니다 😎
'ETC' 카테고리의 다른 글
[ETC] 무중단 배포 전략 (Rolling, Blue/Green, Canary) (1) | 2024.05.15 |
---|---|
[Linux] Cron(크론)이란? (0) | 2024.04.04 |
[Github] Github Action이란? (0) | 2024.03.26 |
[JIRA] Scrum 보드 활용하기 (0) | 2024.01.02 |