Observer 패턴은 마치 각 국가 별 특파원과 같은 역할을 한다.
예를 들어 미국에 있는 소식이 궁금하면, 한국에 있는 기자가 직접 미국에 가지 않더라도 특파원에게 미국 소식을 전파 받을 수 있다.
또한 영국에 있는 소식이 궁금하면, 마찬가지로 영국에 있는 특파원한테 영국 소식을 전파 받으면 된다.
그리고 영국이나 미국에 있는 특파원들도 한국에 특별한 일이 없으면 가지 않더라도 그 곳에서 한국 소식을 받을 수 있다.
그러나 이란이나 아프가니스탄 등 여행 위험 지역에 있는 경우엔 특파원을 따로 두게 되면 위험하기 때문에 특수 안전 교육을 받은 취재진이 파견되는 수 밖에 없다.
위와 마찬가지로, 하나의 주제 (Subject) 가 바뀌면 여러 객체들 (Observer) 이 참고하는 값에 대해 언제든지 갱신되어야 한다.
이러한 역할을 하는 패턴이 바로 Observer 패턴이다.
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
Subject.java
public class NewsTopic implements Subject {
private List<Observer> observers;
private Map<String, String> news;
public NewsTopic() {
this.observers = new LinkedList<>();
this.news = new HashMap<>();
}
@Override
public void registerObserver(Observer observer) {
if (!this.observers.contains(observer)) {
this.observers.add(observer);
this.news.put(observer.getKey(), "-- 공백 --");
}
}
@Override
public void removeObserver(Observer observer) {
if (this.observers.contains(observer)) {
this.observers.remove(observer);
this.news.remove(observer.getKey());
}
}
@Override
public void notifyObservers() {
this.observers.forEach(o -> o.update(this.getNews()));
}
public void setNews(Observer observer, String news) {
this.news.put(observer.getKey(), news);
this.notifyObservers();
}
public String getNews() {
return this.news
.keySet()
.stream()
.map(key -> String.format("[%s] %s\n", key, this.news.get(key)))
.collect(Collectors.joining());
}
}
NewsTopic.java
public interface Observer {
void update(String news);
String getKey();
}
Observer.java
public class UKReporter implements Observer {
private Subject subject;
private String key;
private String news;
public UKReporter(Subject subject, String key) {
this.subject = subject;
this.key = key;
this.news = "";
subject.registerObserver(this);
}
public void fired() {
this.subject.removeObserver(this);
}
public void subscribe() {
this.subject.registerObserver(this);
}
@Override
public void update(String news) {
this.news = news;
System.out.printf("-- <<%s Topic Briefing>> --\n%s\n", this.key, this.news);
}
@Override
public String getKey() {
return this.key;
}
UKReporter.java
public class USAReporter implements Observer {
private Subject subject;
private String key;
private String news;
public USAReporter(Subject subject, String key) {
this.subject = subject;
this.key = key;
this.news = "";
subject.registerObserver(this);
}
public void fired() {
this.subject.removeObserver(this);
}
public void subscribe() {
this.subject.registerObserver(this);
}
@Override
public void update(String news) {
this.news = news;
System.out.printf("-- <%s TODAY NEWS> --\n%s\n", this.key, this.news);
}
@Override
public String getKey() {
return this.key;
}
}
USAReporter.java
public class Main {
public static void main(String[] args) {
NewsTopic news = new NewsTopic();
System.out.println("-- LA 타임즈 뉴스 보도 시작 --");
USAReporter usaReporter = new USAReporter(news, "The L.A. Times");
news.setNews(usaReporter, "LA 다저스 팀 인원 일부 교체될 것");
System.out.println("-- USA 타임즈 뉴스 보도 시작 --");
USAReporter usaReporter2 = new USAReporter(news, "The USA Times");
news.setNews(usaReporter2, "올해 미국은 조 바이든이 이끄는 걸로");
System.out.println("-- 영국 타임즈 뉴스 보도 시작 --");
UKReporter ukReporter = new UKReporter(news, "The United Kingdom Times");
news.setNews(ukReporter, "영국 여왕 올해 내한 예정을 밝혀");
System.out.println("-- 멘체스터 타임즈 뉴스 보도 시작 --");
UKReporter ukReporter2 = new UKReporter(news, "The Manchester Times");
news.setNews(ukReporter2, "올해 멘체스터 경기장 신축될 것");
System.out.println("-- 영국 타임즈 보도 내용 변경 --");
news.setNews(ukReporter, "영국 여왕 올해는 메로나 때문에 내한 힘들 것");
System.out.println("-- USA 타임즈 보도 종료 --");
usaReporter2.fired();
news.notifyObservers();
}
}
Main.java
-- LA 타임즈 뉴스 보도 시작 --
-- <The L.A. Times TODAY NEWS> --
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
-- USA 타임즈 뉴스 보도 시작 --
-- <The L.A. Times TODAY NEWS> --
[The USA Times] 올해 미국은 조 바이든이 이끄는 걸로
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
-- <The USA Times TODAY NEWS> --
[The USA Times] 올해 미국은 조 바이든이 이끄는 걸로
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
-- 영국 타임즈 뉴스 보도 시작 --
-- <The L.A. Times TODAY NEWS> --
[The USA Times] 올해 미국은 조 바이든이 이끄는 걸로
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
[The United Kingdom Times] 영국 여왕 올해 내한 예정을 밝혀
-- <The USA Times TODAY NEWS> --
[The USA Times] 올해 미국은 조 바이든이 이끄는 걸로
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
[The United Kingdom Times] 영국 여왕 올해 내한 예정을 밝혀
... (중략) ...
-- 영국 타임즈 보도 내용 변경 --
-- <The L.A. Times TODAY NEWS> --
[The USA Times] 올해 미국은 조 바이든이 이끄는 걸로
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
[The United Kingdom Times] 영국 여왕 올해는 메로나 때문에 내한 힘들 것
[The Manchester Times] 올해 멘체스터 경기장 신축될 것
-- <The USA Times TODAY NEWS> --
[The USA Times] 올해 미국은 조 바이든이 이끄는 걸로
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
[The United Kingdom Times] 영국 여왕 올해는 메로나 때문에 내한 힘들 것
[The Manchester Times] 올해 멘체스터 경기장 신축될 것
-- <<The United Kingdom Times Topic Briefing>> --
[The USA Times] 올해 미국은 조 바이든이 이끄는 걸로
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
[The United Kingdom Times] 영국 여왕 올해는 메로나 때문에 내한 힘들 것
[The Manchester Times] 올해 멘체스터 경기장 신축될 것
-- <<The Manchester Times Topic Briefing>> --
[The USA Times] 올해 미국은 조 바이든이 이끄는 걸로
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
[The United Kingdom Times] 영국 여왕 올해는 메로나 때문에 내한 힘들 것
[The Manchester Times] 올해 멘체스터 경기장 신축될 것
-- USA 타임즈 보도 종료 --
-- <The L.A. Times TODAY NEWS> --
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
[The United Kingdom Times] 영국 여왕 올해는 메로나 때문에 내한 힘들 것
[The Manchester Times] 올해 멘체스터 경기장 신축될 것
-- <<The United Kingdom Times Topic Briefing>> --
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
[The United Kingdom Times] 영국 여왕 올해는 메로나 때문에 내한 힘들 것
[The Manchester Times] 올해 멘체스터 경기장 신축될 것
-- <<The Manchester Times Topic Briefing>> --
[The L.A. Times] LA 다저스 팀 인원 일부 교체될 것
[The United Kingdom Times] 영국 여왕 올해는 메로나 때문에 내한 힘들 것
[The Manchester Times] 올해 멘체스터 경기장 신축될 것
실행 결과
subscribe()
, fired()
메소드를 추가했다.public
접근자 setNews()
, getNews()
메소드를 사용하면 Observer 관점에서 데이터 은닉을 어길 수 밖에 없다.notifyObserver()
를 실행해야 하기 때문에 수행 비용의 낭비가 초래된다.Java 9 이전 버전을 사용하면 Observable 클래스, Observer 인터페이스를 제공한다.
하지만 상속 등 번거로운 작업들이 많아지기 때문에 Deprecated 가 된 듯 하다.
더욱 자세한 Deprecated 원인은 다음과 같다.
아래는 Java SE 9 에서 언급한 Deprecated 원인 내용이다.
java.util.concurrent
패키지의 자료구조를 사용하는 것이 좋다.