[디자인 패턴] 옵저버 패턴(Observer Pattern)이란?
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 에게만 알림이 가는 것을 확인할 수 있다.
출처
긴 글 읽어주셔서 감사합니다 🍀
잘못 작성된 내용은 피드백 주시면 반영하겠습니다 😎