옵저버(Observer)
는 관찰자라는 의미를 갖고있는데, 이 패턴이 변화가 발생하게 됨에 따라 객체들에게 알려주는 역할을 하기 때문에 옵저버 패턴
이라는 이름을 가지게 되었습니다.
옵저버 패턴(Observer Pattern)
이란, 어떤 주제(속성, 값) 객체가 변화하게 되면 그와 연관된(1개 이상)의 객체 들에게 변화를 알리는 패턴입니다. 여기서 주제 객체는 관찰 대상, 그와 연관된 객체는 옵저버라고 부릅니다. 따라서 옵저버들은 관찰 대상을 관찰하다가 관찰 대상이 변화하면 그에 따른 이벤트 등을 처리합니다. 옵저버 패턴
은 지난 시간에 소개한 세 가지 패턴(생성, 구조, 행동)중에서 행동 패턴(Behavioral)에 속합니다.
관찰 대상과 옵저버들은 느슨한 연결을 가집니다. 느슨한 연결
이란 객체 사이에 상호작용이 이루어지지만 직접적인 관계는 없는 상태를 의미합니다. 느슨한 연결을 가지게 됨으로써 옵저버를 언제든지 추가/삭제 해도 되고 다른 용도로 재사용이 가능합니다. 그리고 옵저버나 관찰 대상이 변화해도 서로에게 영향을 전혀 미치지 않기 때문에 유지보수가 간단하다는 장점을 가집니다.
옵저버 패턴을 간단하게 구현하기 위해 한가지 예시를 들어보겠습니다. 바로 신문과 구독자입니다. 신문사가 어떤 정보를 담아서 신문을 배포하면 그 신문을 읽기 위해서 구독을 하고 읽기 싫다면 구독을 해지하기도 합니다. 또 신문사에서 기고한 정보들을 읽으면 구독자들을 그 정보를 알게되는 이런 시스템이 옵저버 패턴을 가장 잘 나타내주고 있습니다. 여기서 신문사가 정보제공자 즉, Subject, 주제가 되고, 구독자들이 Observer가 되는 것 입니다.
그렇다면 위의 예시를 따라서 한 번 구현해보도록 하겠습니다.
먼저 Publisher 입니다. 퍼블리셔가 위에서 설명한 Subject가 됩니다. 역할만을 명시하기 위해서 인터페이스로 프로그래밍 했습니다.
package observer;
public interface Publisher {
//퍼블리셔는 구독자를 얻고 잃고, 정보를 알리는 역할을 합니다.
public void registerSubscriber(Subscriber s);
public void removeSubscriber(Subscriber s);
public void notifySubscriber();
}
다음으로 볼 것은 옵저버 역할을 하는 구독자, Subscriber입니다. 마찬가지로 역할만을 나타내기 위해 인터페이스로 프로그래밍 했습니다.
package observer;
public interface Subscriber {
//구독자는 정보(기사)를 받아서 업데이트 합니다.
public void update(String article);
}
이제 위에서 인터페이스로 프로그래밍 했던 것들을 구체적으로 구현할 차례입니다. 먼저 Publisher를 구현한 PublishingSystem부터 보겠습니다.
package observer;
import java.util.ArrayList;
public class PublishingSystem implements Publisher {
private ArrayList subscribers;
private String article;
public PublishingSystem() {
subscribers = new ArrayList();
}
//구독자 추가
@Override
public void registerSubscriber(Subscriber s) {
subscribers.add(s);
}
//구독자 제거
@Override
public void removeSubscriber(Subscriber s) {
int i = subscribers.indexOf(s);
if (i >= 0) {
subscribers.remove(s);
}
}
//정보가 갱신되면 구독자들에게 알립니다.
@Override
public void notifySubscriber() {
for (int i = 0; i < subscribers.size(); i++) {
Subscriber subscriber = (Subscriber) subscribers.get(i);
subscriber.update(article);
}
}
public void updateArticles() {
notifySubscriber();
}
public void setArticle(String article) {
this.article = article;
updateArticles();
}
}
이제 Subscriber를 추가해야하는데, 저는 구체적으로 Ann, Jay, John이라는 세명을 구현했습니다. 다 같은 코드이므로 Ann의 코드를 보겠습니다.
여러 방법이 있겠지만 저는 초기화를 하는 동시에 구독자 명단에 등록시키기로 했습니다. 그래서 초기화 부분에 등록 과정을 넣어주었습니다.
package observer;
public class Ann implements Subscriber {
private String article;
private Publisher publisher;
//생성과 함께 구독자 목록에 등재
public Ann(Publisher publisher) {
this.publisher = publisher;
publisher.registerSubscriber(this);
}
@Override
public void update(String article) {
this.article = article;
display();
}
private void display() {
System.out.println("Ann은 \'" + article + "\' 이라는 기사를 읽고 정보를 얻었습니다.");
}
}
이번에는 실행을 할 메인 메소드 부분입니다. 클래스명은 뉴스가판대(Newsstand)로 지었습니다.
package observer;
public class Newsstand {
public static void main(String[] args) {
PublishingSystem publishingSystem = new PublishingSystem();
Subscriber Ann = new Ann(publishingSystem);
Subscriber Jay = new Jay(publishingSystem);
Subscriber John = new John(publishingSystem);
publishingSystem.setArticle("솔의 눈 대인기!");
//Jay가 구독 취소
publishingSystem.removeSubscriber(Jay);
System.out.println("==================");
publishingSystem.setArticle("총알보다 빠른 거북이 발견");
}
}
전체적인 코드를 봤으니, 실행결과를 확인해보도록 하겠습니다.
처음에 3명 모두 구독상태였다가 Jay가 구독을 취소한 이후로는 앤과 존만이 소식을 받아볼 수 있습니다.
이것이 옵저버 패턴의 구현입니다. 이것은 간단하게 구현을 한 것이고, 다른 방식으로도 얼마든지 구현이 가능합니다. 다만 중요한 것은 변화가 발생할 때마다 옵저버들에게 알려야 하는 패턴임을 알고있으면 됩니다.