한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many)의존성을 정의한다.
옵저버 패턴에서는 다수에게 상태를 전달해주는 주제
와 주제에게 상태를 전달받는 여러개의 옵저버
의 관계로 정의된다.
여기서 중요한 점은 주제와 옵저버는 상호작용은 하지만 서로에 대해 잘 모른다는 것이다.
서로에 대해 잘 모른다는 것이 정확히 무슨 뜻일까?
주제는 옵저버가 Obsever interface
를 구현하고 있다는 사실만 알고 다른 것은 모른다는 것을 말한다.
즉 Observer 인터페이스만 구현하고 있다면 어떤 클래스든 주제의 옵저버로 추가할 수 있다. 주제는 디자인 원칙 중 하나인
구현보다는 구성을 활용한다.
를 따라서 Observer 형식의 필드를 가지고 옵저버를 관리하므로 새로운 클래스를 옵저버로 추가한다고 해도 주제의 코드를 변경할 필요없이 인터페이스로 옵저버를 활용하면 된다.
즉, 어떤 인터페이스를 구현했는지만 알고 있다는 것은 각각의 코드가 변경되어도 전혀 영향을 끼치지 않는다는 것을 의미한다.
옵저버패턴은 주제와 옵저버의 느슨한결합을 통해 재사용성을 보장하며 일대다 관계를 정의한다.
옵저버 패턴에서 갱신해주는 내용을 저장하고 있는 주인은 주제다.
그리고 내용이 갱신되었을 때 옵저버들은 주제가 갱신해주기를 기다리기 때문에 의존성을 가진다고 말할 수 있다.
동일한 데이터를 여러 객체에서 제어하는 것보다 하나의 상태데이터를 가지고 여러 객체에 갱신시켜 줌(의존성을 가지고)으로써 더 깔끔한 객체지향 디자인을 만들 수 있다.
+ 옵저버들한테 연락을 돌리는 순서에 의존해서는 안된다.
순서가 바뀌었다고 다른 결과가 나온다면 각자의 interface외의 정보도 신경써야하기에 느슨한 결합이라고 볼 수 없다.
Suject
interface Subject
{
public void registObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObserver();
public void setChange();
}
class ConcreteSubject : Subject
{
List<Observer> observers;
int State;
bool change;
public ConcreteSubject()
{
this.observers = new List<Observer>();
this.State = 0;
this.change = false;
}
public void registObserver(Observer o)
{
observers.Add(o);
}
public void removeObserver(Observer o)
{
this.observers.Remove(o);
}
public void notifyObserver()
{
if (change == true)
{
for (int i = 0; i < observers.Count; i++)
{
observers[i].update(State);
}
}
}
public void updateObserver(int state)
{
this.State = state;
setChange();
notifyObserver();
}
public void setChange()
{
change = true;
}
}
Observer
interface Observer
{
public void update(int state);
}
class ConcreteObserver : Observer
{
Subject subject;
int observerState;
public ConcreteObserver(Subject subject)
{
//바로 subjectd에 observer로 등록
this.subject = subject;
subject.registObserver(this);
observerState = 0;
}
public void update(int state)
{
//상태갱신
this.observerState = state;
display();
}
public void display()
{
Console.WriteLine("State update : {0}", observerState);
}
}
}
Test
static void Main(string[] args)
{
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver(subject);
ConcreteObserver observer2 = new ConcreteObserver(subject);
subject.updateObserver(3);
subject.updateObserver(5);
}
결과
옵저버패턴에서는 옵저버에게 상태를 갱신하는 방법에 2가지가 있다.
- 푸시방식 -> 주제가 상태를 보내줌 update(state)
- 풀방식 ->옵저버가 상태를 가져감 update(subject)
위의 구현은 푸시방식으로 옵저버패턴을 구현한 것이고 풀 방식으로 변경하기 위해선 다음 코드만 변경하면 된다.
class ConcreteSubject : Subject {
...
public void notifyObserver()
{
if (change == true)
{
for (int i = 0; i < observers.Count; i++)
{
//observers[i].update(State);
observers.[i].update(this);
}
}
}
//get 메소드 추가
public void getState(){
return State;
...
}
interface Observer
{
//public void update(int state);
public void update(Subject subject);
}
class ConcreteObserver : Observer {
public void update(Subject subject)
{
subject = (ConcreteSuject)suject;
this.state = suject.getState();
}
...
}