객체 사이에 일 대 다의 의존 관계를 정의해 두어, 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그변화를 통지받고 자동으로 갱신될 수 있게 만듭니다.
종속자(Dependent), 게시-구독(Publish-Subscribe)
객체 간에 일관성을 유지하도록 해야 합니다. 일관성 관리를 위해서 객체 간의 결합도를 높이고 싶지는 않습니다.
예를들어, 그래픽 사용자 인터페이스 툴킷은 표현 부분과 이에 대응하는 데이터를 분리합니다. 응용프로그램 자료와 표현을 정의하는 클래스는 독립적으로 재사용 할 수 있습니다. 그러나 이들은 함께 동작해야합니다.
아래는 그림으로 예시
감시자 패턴은 주체(Subject)와 감시자(observer)입니다. 주체는 여러게의 감시가 있을 수있습니다. 각 감시자는 주체의 상태와 자신의 상태를 동기화시키기 위해 주체의 상태를 알아봅니다.
이런 종류의 상호작용을 게시 - 구독 관계라고 합니다. 주체는 상태 변경에 대한 통보를 하는 것이므로 누가 감시자인지 모른 채 통보를 발송합니다. 불특정 다수의 감시자가 이 통보를 수신하기 위해 구독을 신청하는 것입니다.
다음 상황 중 어느 한 가지에 속하면 감시자 패턴을 사용합니다.
변경을 유발하는 Observer 객체가 어떻게 주체한테서 변경 통보를 받을 때까지 수정을 늦추는지 알아봅시다.
Notify() 연산은 주체로만 호출되는 것이 아니고, 감시자나 다른 객체들로도 호출이 가능합니다.
감시자 패턴을 사용하게 되면 주체 및 감시자 모두를 독립적으로 변형하기 쉽습니다. 감시자를 재사용하지 않고도 주체를 재사용 할 수 있고, 주체 없이도 감시자를 재사용 할 수 있습니다. 또한 주체나 감시자의 수정 없이도 감시자를 추가할 수 있습니다.
감시자 패턴을 사용함으로써 얻을 수 있는 그 밖의 이익은 다음과 같습니다.
package study.designpattern;
import java.util.ArrayList;
import java.util.List;
class Subject {
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void myNotify() {
for (Observer observer : observers) {
observer.update(this);
}
}
List<Observer> observers = new ArrayList<>();
}
interface Observer {
void update(Subject theChangedSubject);
}
class ClockTimer extends Subject {
public int getHour() {
return 0;
}
public int getMinute() {
return 0;
}
public int getSecond() {
return 0;
}
void trick() {
notify();
}
}
interface Widget {
void draw();
}
class DigitalClock implements Widget, Observer {
public DigitalClock(ClockTimer subject) {
this.subject = subject;
this.subject.attach(this);
}
// 1. 자바에는 소멸자 개념이 없고 가비지 켤랙션에서 삭제될때 발생하는 메시지라 추후 재대로 동작하는지 확인 필요
// 2. Effect 자바 항목에서는 finalize 를 사용하지말라고 권고함
@Override
protected void finalize() throws Throwable {
this.subject.detach(this);
super.finalize();
}
@Override
public void update(Subject theChangedSubject) {
if(theChangedSubject == this.subject) {
draw();
}
}
@Override
public void draw() {
int hour = subject.getHour();
int minute = subject.getMinute();
System.out.println("DigitalClock.draw");
// 등 디지털 시간을 표기합니다.
}
private ClockTimer subject;
}
// 확장됩니다.
class AnalogClock implements Widget, Observer {
public AnalogClock(ClockTimer subject) {
this.subject = subject;
this.subject.attach(this);
}
// 1. 자바에는 소멸자 개념이 없고 가비지 켤랙션에서 삭제될때 발생하는 메시지라 추후 재대로 동작하는지 확인 필요
// 2. Effect 자바 항목에서는 finalize 를 사용하지말라고 권고함
@Override
protected void finalize() throws Throwable {
this.subject.detach(this);
super.finalize();
}
@Override
public void update(Subject theChangedSubject) {
// ...
}
@Override
public void draw() {
System.out.println("AnalogClock.draw");
// ...
}
private ClockTimer subject;
}
public class ObserverPattern {
public static void main(String[] args) {
ClockTimer timer = new ClockTimer();
AnalogClock analogClock = new AnalogClock(timer);
DigitalClock digitalClock = new DigitalClock(timer);
timer.myNotify();
// timer의 시간이 바뀔 때마다 두 개의 시계가 수정되어 다시 출력됩니다.
}
}
복잡한 갱신의 의미 구조를 캡슐화함으로써, ChageManager 객체는 주체와 감시자 사이의 중재자 역할을 합니다. ChangeManager 객체는 단일체패턴을 써서 시스템에 하나만 존재하고 전역적으로 접근이 가능하도록 만들 수 있습니다.