Pattern - Observer
의도
객체 사이에 일 대 다의 의존 관계를 정의해 두어, 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그 변화를 통지받고 자동으로 갱신될 수 있게 만듭니다.
동기
어떤 하나의 시스템을 서로 연동되는 클래스 집합으로 분할 했을 때 발생하는 공통적인 부작용은 관련된 객체 간의 일관성을 유지하도록 해야 한다는 것입니다. 그렇다고 이 일관성 관리를 위해서 객체 간의 결합도를 높이고 싶지는 않습니다. 그렇게 되면 각 클래스의 재사용성이 떨어지기 때문입니다. 감시자 패턴은 이런 관련성을 관리하는 패턴입니다. 이 패턴에서 중요한 객체는 주체와 감시자입니다. 주체는 독립된 여러 개의 감시가 있을 수 있습니다. 모든 감시자는 주체의 상태 변화가 있을 때마다 이 변화를 통보받습니다. 이런 종류의 상호작용을 게시-구독 관계라고 합니다. 주체는 상태 변경에 대한 통보를 하는 것이므로 누가 감시자인지 모른 채 통보를 발송합니다. 불특정 다수의 감시자가 이 통보를 수신하기 위해서 구독을 신청하는 것입니다.
활용성
- 어떤 추상 개념이 두 가지 양상을 갖고 하나가 다른 하나에 종속적일 때. 각 양상을 별도의 객체로 캡슐화하여 이들 각각을 재사용할 수 있습니다.
- 한 객체에 가해진 변경으로 다른 객체를 변경해야 하고, 프로그래머들은 얼마나 많은 객체들이 변경되어야 하는지 몰라도 될 때
- 어떤 객체가 다른 객체에 자신의 변화를 통보할 수 있는데, 그 변화에 관심있어하는 객체들이 누구인지에 대한 가정없이도 그러한 통보가 될 때
항목에 대한 설명
-
Subject
감시자들을 알고 있는 주체입니다. 임의 개수의 감시자 객체는 주체를 감시할 수 있습니다. 주체는 감시자 객체를 붙이거나 떼는 데 필요한 인터페이스를 제공합니다. -
Observer
주체에 생긴 변화에 관심있는 객체를 갱신하는데 필요한 인터페이스를 정의합니다. 이로써 주체의 변경에 따라 변화되어야 하는 객체들의 일관성을 유지합니다. -
ConcreteSubject ConcreteObserver 객체엑 알려주어야 하는 상태를 저장힙니다. 또한 이 상태가 변경될 때 감시자에게 변경을 통보합니다.
-
ConcreteObserver ConcreteSubject 객체에 대한 참조자를 관리합니다. 주체의 상태와 일관성을 유지해야하는 상태를 저장힙니다. 주체의 상태와 감시자의 상태를 일관되게 유지하는 데 사용하는 갱신 인터페이스를 구현합니다.
협력방법
- ConcreteSubject 는 Observer 의 상태와 자신의 상태가 달라지는 변경이 발생할 때마다 감시자에게 통보합니다.
- Concrete subject 에서 변경이 통보된 후, ConcreteObserver 는 필요한 정보를 주체에게 질의하여 얻어옵니다. ConcreteObserver 는 이 정보를 이용해서 주체의 상태와 자신의 상태를 일치시킵니다.
결과
- Subject와 Observer 클래스 간에게는 추상적인 결합도만 존재합니다.
- 브로드캐스트(broadcast) 방식의 교류를 가능하게 합니다.
- 예측하지 못한 정보를 갱신합니다.
코드 예제 – 기본 샘플
package designpattern.gof_observer.sample01;
import designpattern.gof_observer.sample01.publisher.NewsMachine;
import designpattern.gof_observer.sample01.subscriber.AnnualSubscriber;
import designpattern.gof_observer.sample01.subscriber.EventSubscriber;
public class MainClass {
public static void main(String[] args) {
NewsMachine newsMachine = new NewsMachine();
AnnualSubscriber annualSubscriber = new AnnualSubscriber(newsMachine);
EventSubscriber eventSubscriber = new EventSubscriber(newsMachine);
newsMachine.setNewsInfo("오늘 한파", "전국 영하 18도 입니다.");
newsMachine.setNewsInfo("벛꽃 축제합니다", "다같이 벚꽃보러~");
}
}
package designpattern.gof_observer.sample01.publisher;
import designpattern.gof_observer.sample01.subscriber.Observer;
import java.util.ArrayList;
public class NewsMachine implements Publisher {
private ArrayList<Observer> observers;
private String title;
private String news;
public NewsMachine() {
observers = new ArrayList<>();
}
@Override
public void add(Observer observer) {
observers.add(observer);
}
@Override
public void delete(Observer observer) {
int index = observers.indexOf(observer);
observers.remove(index);
}
@Override
public void notifyObserver() {
for (Observer observer : observers) {
observer.update(title, news);
}
}
public void setNewsInfo(String title, String news) {
this.title = title;
this.news = news;
notifyObserver();
}
public String getTitle() {
return title;
}
public String getNews() {
return news;
}
}
package designpattern.gof_observer.sample01.publisher;
import designpattern.gof_observer.sample01.subscriber.Observer;
public interface Publisher {
public void add(Observer observer);
public void delete(Observer observer);
public void notifyObserver();
}
package designpattern.gof_observer.sample01.subscriber;
import designpattern.gof_observer.sample01.publisher.Publisher;
public class AnnualSubscriber implements Observer {
private String newsString;
private Publisher publisher;
public AnnualSubscriber(Publisher publisher) {
this.publisher = publisher;
publisher.add(this);
}
@Override
public void update(String title, String news) {
this.newsString = title + " \n -------- \n " + news;
display();
}
private void display() {
System.out.println("\n\n오늘의 뉴스\n============================\n\n" + newsString);
}
}
package designpattern.gof_observer.sample01.subscriber;
import designpattern.gof_observer.sample01.publisher.Publisher;
public class EventSubscriber implements Observer {
private String newsString;
private Publisher publisher;
public EventSubscriber(Publisher publisher) {
this.publisher = publisher;
publisher.add(this);
}
@Override
public void update(String title, String news) {
newsString = title + "\n------------------------------------\n" + news;
display();
}
public void display() {
System.out.println("\n\n=== 이벤트 유저 ===");
System.out.println("\n\n" + newsString);
}
}
package designpattern.gof_observer.sample01.subscriber;
public interface Observer {
public void update(String title, String news);
}