위키백과에 의하면 옵저버 패턴의 정의는 다음과 같다.
옵서버 패턴(observer pattern)은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다.
좀 더 풀어서 말하면 어떤 이벤트가 발생했을 때 주제(Subject)라 불리는 객체가 다른 객체 리스트(옵저버)에 자동으로 알림을 보내야 하는 상황에서 사용한다. 예를 들어서 자신이 구독한 유튜브 채널에서 영상이 올라오면 구독자들에게 알림이 오는데 이런 상황에서 옵저버 패턴을 사용한다. 구독자(옵저버)들이 유튜버(주제 객체)를 관찰하고 있는 것이다.
옵저버 패턴 구조
Observer : 데이터의 변경을 통보받는 인터페이스
Subject : ConcreteObserver 객체를 관리하는 인터페이스
ConcreteObserver : Observer의 구현체. ConcreteSubject의 변경을 통보받는 클래스
ConcreteSubject : Subject의 구현체. 상태 변경을 통보하는 클래스
옵저버 패턴을 사용하면 인터페이스를 통해 객체 간 의존성을 최소화시킬 수 있다. Subject 객체와 ConcreteObserver 객체는 Obvserver 인터페이스를 통해 의존관계가 성립되고 있다. 즉 Subject 객체는 ConcreateObserver에 대해 모르기 때문에 Observer 인터페이스를 구현한 객체는 언제든지 새로 추가되거나 제거될 수 있다. 이런 변경 작업에도 Subject 객체는 영향을 받지 않는다.(개방-폐쇄 원칙) 이러한 관계를 느슨한 결합이라고 한다.
옵저버 패턴 예시
유튜버와 구독자가 있다. 유튜버가 영상을 업로드하면 구독자에게 알림이 가야 한다. 예시를 직관적으로 보기 위해 Subject와 Observer 인터페이스는 이름을 그대로 사용하고 구현체 이름만 예시에 맞게 변경하였다.
Subject
public interface Subject {
void subscribe(Observer subscriber);
void unSubscribe(Observer subscriber);
void upload(String title);
}
Youtuber 클래스가 구현할 인터페이스로 구독자(옵저버)를 등록, 삭제하는 메서드와 영상을 업로드하여 상태 변경을 전파하는 메소드를 가지고 있다.
Youtuber (ConcreteSubject)
public class Youtuber implements Subject {
private final String name;
private final List<Observer> subscribers = new ArrayList<>();
public Youtuber(String name) {
this.name = name;
}
@Override
public void subscribe(Observer subscriber) {
subscribers.add(subscriber);
}
@Override
public void unSubscribe(Observer subscriber) {
subscribers.remove(subscriber);
}
@Override
public void upload(String title) {
subscribers.forEach(subscriber -> subscriber.receiveNotification(name, title));
}
}
Subject 인터페이스의 구현체이다. 이름과 옵저버 리스트를 필드로 가지고 있다. 옵저버 리스트에 옵저버를 등록 및 삭제하는 메서드를 오버라이딩 했으며 upload() 메서드를 호출하면 모든 구독자(옵저버)에게 자신의 이름과 영상 제목을 전파한다.
Observer
public interface Observer {
void receiveNotification(String name, String title);
}
Subscriber 클래스가 구현할 인터페이스로 Youtuber 클래스가 영상을 업로드하면 이름과 영상 제목을 통보받는 메서드를 가지고 있다.
Subscriber (ConcreteObserver)
public class Subscriber implements Observer {
private final String name;
public Subscriber(String name) {
this.name = name;
}
@Override
public void receiveNotification(String name, String title) {
System.out.println("<" + this.name + "님에게 알림 도착>");
System.out.println(name + "님이 영상을 올렸습니다!");
System.out.println("영상 제목 [" + title + "]");
System.out.println();
}
}
Observer 인터페이스의 구현체이다. receiveNotification을 오버라이딩해서 Subject 구현체의 upload() 알림을 받고 있다.
구독자 등록 및 영상 업로드
public static void main(String[] args) {
Observer member1 = new Subscriber("member1");
Observer member2 = new Subscriber("member2");
Observer member3 = new Subscriber("member3");
Subject woowaTV = new Youtuber("woowaTV");
woowaTV.subscribe(member1);
woowaTV.subscribe(member2);
woowaTV.subscribe(member3);
}
이제 main 메서드에서 실행을 해볼 차례이다. 우선 다음처럼 Subscriber 객체를 생성한 뒤 Youtuber 객체에 등록했다. member1, member2, member3이 woowaTV를 구독했다.
그리고 woowaTV가 영상을 올리면 구독자들이 알림을 받는다.
woowaTV.upload("우아한테크코스 온라인 설명회");
member2가 구독을 취소하고 woowaTV가 다음 영상을 올렸다.
woowaTV.unSubscribe(member2);
woowaTV.upload("테코톡 더즈의 옵저버 패턴");
그럼 member2를 제외한 구독자에게만 알림이 간다.
이처럼 옵저버 패턴은 어떤 객체의 변화나 행동을 어러 객체에서 주시해야 할 때 유용하다. 하지만 주제 객체와 옵저버 객체의 관계 정의를 잘해줘야 하며 데이터 배분에 문제가 생기면 큰 문제로 이어질 수도 있다. 또 코드가 복잡해지면 프로그램에서 코드가 서로 어떻게 상호작용하는지 알기 어려워져 상태 관리가 힘들어질 수도 있다.
'JAVA' 카테고리의 다른 글
IllegalArgumentException vs IllegalStateException (0) | 2022.03.31 |
---|---|
[JAVA] Enum으로 if문 제거하기 (0) | 2022.03.16 |
[AssertJ] Iterable and array assertions 활용 (컬렉션 테스트) (0) | 2022.03.03 |
[자바] 객체 내부 컬렉션 데이터 보호하기 (0) | 2022.02.26 |
자바 JDK, JRE, JVM 간단 정리 (0) | 2022.02.21 |