[Design Pattern] 옵저버 패턴

Observer Pattern

옵져버 패턴은 객체 사이에 일 : 다 의존 관계를 정의해두어 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그 변화를 통지받고 자동으로 갱신될 수 있게 만드는 패턴이다. 이런 종류의 상호작용을 게시 - 구독 관계라 한다.

활용성

  1. 한 객체에 가해진 변경으로 다른 객체를 변경해야 하고 프로그래머들은 얼마나 많은 객체들이 변경되어야 하는지 몰라도 될 때
  2. 어떤 객체가 다른 객체에 자신의 변화를 통보할 수 있는데 그 변화에 관심있어 하는 객체들이 누구인지에 대한 가정 없이도 그러한 통보가 될 때

옵저버 패턴 사용에 따른 결과

Subject - Observer간 추상적 결합

Subject 클래스와 Observer 클래스의 약한 결합덕분에 시스템에서 이들의 서브 클래스를 수용할 수 있다.

브로드 캐스트 방식의 교류

구체적인 수신자를 송신자에서 알 필요가 없기 때문에 브로드 캐스트 방식의 통신을 구현할 수 있다.

예측하지 못한 정보 갱신

종속성을 기준으로 상태를 갱신하기 때문에 불필요한 갱신이 일어날 수 있다. 또한 추적도 까다로울 수 있다. 어떤 정보가 갱신되었는지는 옵저버가 무엇이 변경되었는지 알 수 있게 해 주는 별도의 프로토콜이 추가되지 않는 한 변경을 유추하는 작업은 어렵다.

옵저버 패턴 vs 중재자 패턴

옵저버 패턴을 사용해서 중재자 패턴을 구현할 수 있다. 중재자패턴에서는 Colleague가 중재자를 참조할 수 있다. 중재자가 Colleague를 참조하는 것은 옵저버 패턴의 형태와 동일하다. 즉 어떤 참조를 구독하고 있는 경우에서 알림을 보낼때는 옵저버 패턴을 사용하고 다수의 클라이언트간에서 상호간의 통신을 해야할 때 즉, 1 : N의 관계보다 1 : 1의 단순한 관계를 유지해야할 때는 중재자 패턴을 사용한다.

옵저버

구조

옵저버의 구조는 다음과 같다.

Observer Pattern Diagram

C++ 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void ObserverPattern::ObserverPattern()
{
Subject* concreteSubject = new ConcreteSubject();
Observer* observer1 = new ConcreteObserver("observer1");
Observer* observer2 = new ConcreteObserver("observer2");
Observer* observer3 = new ConcreteObserver("observer3");
Observer* observer4 = new ConcreteObserver("observer4");

concreteSubject->attach(observer1);
concreteSubject->attach(observer2);
concreteSubject->attach(observer3);

concreteSubject->notify("Notification 1");

concreteSubject->detach(observer1);

concreteSubject->attach(observer4);

concreteSubject->notify("Notification 2");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Subject
{
public:
virtual ~Subject() {}
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify(std::string notification) = 0;
};

class ConcreteSubject : public Subject
{
std::set<Observer*> observers;
public:
void attach(Observer* observer) override
{
observers.insert(observer);
}

void detach(Observer* observer) override
{
observers.erase(observers.find(observer));
}

void notify(std::string notification) override
{
for(Observer* observer : observers)
observer->update(notification);
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Observer
{
public:
virtual ~Observer() {}
virtual void update(std::string notification) = 0;
};

class ConcreteObserver : public Observer
{
std::string name;
public:
ConcreteObserver(std::string name) : name(name) {}
~ConcreteObserver() {}
void update(std::string notification) override
{
std::cout<<name<<" received : "<<notification<<std::endl;
}
};

java 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Client {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Observer observer1 = new ConcreteObserver("observer 1");
Observer observer2 = new ConcreteObserver("observer 2");
Observer observer3 = new ConcreteObserver("observer 3");
Observer observer4 = new ConcreteObserver("observer 4");

subject.attach(observer1);
subject.attach(observer2);
subject.attach(observer3);

subject.notify("Notification 1");

subject.detach(observer1);
subject.attach(observer4);

subject.notify("Notification 2");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notify(String notification);
}

public class ConcreteSubject implements Subject{
private List<Observer> observers;

public ConcreteSubject() {
observers = new ArrayList<>();
}

@Override
public void attach(Observer observer) {
observers.add(observer);
}

@Override
public void detach(Observer observer) {
observers.remove(observer);
}

@Override
public void notify(String notification) {
for(Observer observer : observers)
observer.update(notification);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface Observer {
void update(String state);
}

public class ConcreteObserver implements Observer{
private String name;

public ConcreteObserver(String name) {
this.name = name;
}

@Override
public void update(String state) {
System.out.println(name + " received : " + state);
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof ConcreteObserver))
return false;
ConcreteObserver concreteObserver = (ConcreteObserver)obj;
return this.name.equals(concreteObserver.name);
}
}
Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/27/Design%20Pattern/ObserverPattern/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.