디자인 패턴 톺아보기 - Observer Pattern

Updated:

1. 옵저버 패턴(Observer Pattern) 이란?

GOF 에서 말하는 옵저버 패턴의 목적은 아래와 같습니다.

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

객체 사이에 일 대 다의 의존 관계를 정의해 두어, 어떤 객체의 상태가 변할 때 그 객체의 의존성을 가진 다른 객체들이 그 변화를 통지받고 자동으로 갱신될 수 있게 만듭니다.

1.1. 구조

Sample / Sequence Diagram

image

  • Subject
    • 감시자들을 알고 있는 주체입니다. 임의 개수의 감시자 객체는 주체를 감시할 수 있습니다. 주체는 감시자 객체를 붙이거나 떼는 데 필요한 인터페이스를 제공합니다.
  • Observer
    • 주체에 생긴 변화에 관심 있는 객체를 갱신하는 데 필요한 인터페이스를 정의합니다. 이로써 주체의 변경에 따라 변화되어야 하는 객체들의 일관성을 유지합니다.
  • ConcreteSubject
    • ConcreteObserver 객체에게 알려주어야 하는 상태를 저장합니다. 또한 이 상태가 변경될 때 감시자에게 변경을 통보합니다.
  • ConcreteObserver
    • ConcreteSubject 객체에 대한 참조자를 관리합니다. 주체의 상태와 일관성을 유지해야 하는 상태를 저장합니다. 주체의 상태와 감시자의 상태를 일관되게 유지하는 데 사용하는 갱신 인터페이스를 구현합니다.

1.2. 사용 방법

  • ConcreteSubject는 Observer의 상태와 자신의 상태가 달라지는 변경이 발생할 때마다 감시자에게 통보합니다.
  • concrete subject에서 변경이 통보된 후, ConcreteObserver는 필요한 정보를 주체에게 질의하여 얻어옵니다. ConcreteObserver는 이 정보를 이용해서 주체의 상태와 자신의 상태를 일치시킵니다.

    1.3. 장/단점

  • Advantages (+)
    • Decouples subject from observers.
    • Makes adding/withdrawing observers easy.
  • Disadvantages (–)
    • Can make the update behavior complex.

1.4. 고려사항

  • Consider the left design (problem):
    • Tight coupling between subject and dependents.
  • Consider the right design (solution):
    • Loose coupling between subject and observers.

2. 옵저버 패턴(Adapter Pattern) 사용예시

옵저버 패턴은 다음 경우에 사용합니다.

  • 어떤 추상 개념이 두 가지 양상을 갖고 하나가 다른 하나에 종속적일 때
  • 한 객체에 가해진 변경으로 다른 객체를 변경해야 하고, 프로그래머들은 얼마나 많은 객체들이 변경되어야 하는지 몰라도 될 때
  • 어떤 객체가 다른 객체에 자신의 변화를 통보할 수 있는데, 그 변화에 관심 있어 하는 객체들이 누구인지에 대한 가정 없이도 그러한 통보가 될 때

2.1. GOF 패턴

2.1.1. Subject

abstract class Subject { 
	private List<Observer> observers = new ArrayList<Observer>();
	
	// Registration interface.
	public void attach(Observer o) { 
		observers.add(o);
	} 
	
	// Notification interface.
	// notify() is already used by the Java Language (to wake up threads).
	public void notifyObservers() { 
		for (Observer o : observers) {
			o.update();
		}
	} 
} 

2.1.2. Observer

abstract class Observer { 
	// Synchronizing observer's state with subject's state.
	public abstract void update();
} 

2.1.3. ConcreteSubject

class Subject1 extends Subject { 
	private int state = 0;
	
	//
	public int getState() { 
		return state;
	} 
	
	void setState(int state) { 
		this.state = state;
		System.out.println("Subject1 : State changed to : " + state + "\n           Notifying observers ...");
	
		// Notifying observers that state has changed.
		notifyObservers();
	} 
} 

2.1.4. ConcreteObserver

class Observer1 extends Observer { 
	private int state;
	private Subject1 subject;
	
	public Observer1(Subject1 subject) { 
		this.subject = subject;
		// Registering this observer on subject.
		subject.attach(this); 
	} 
	
	public void update() { 
		this.state = subject.getState();
		System.out.println("Observer1: State updated to : " + this.state);
	} 
} 

class Observer2 extends Observer { 
	private int state;
	private Subject1 subject;
	
	public Observer2(Subject1 subject) { 
		this.subject = subject;
		// Registering this observer on subject.
		subject.attach(this); 
	} 
	
	public void update() { 
		this.state = subject.getState();
		System.out.println("Observer2: State updated to : " + this.state);
	} 
} 

2.1.5. Main

public class Main{

	// Running the Client class as application.
	public static void main(String[] args) {

		Subject1 s1 = new Subject1();
		// Creating observers and registering them on subject1.
		Observer o1 = new Observer1(s1);
		Observer o2 = new Observer2(s1);
		
		System.out.println("Changing state of Subject1 ...");
		s1.setState(100);
		
	} 
}

결과는 아래와 같습니다.

Changing state of Subject1 ...
Subject1 : State changed to : 100
           Notifying observers ...
Observer1: State updated to : 100
Observer2: State updated to : 100

참고 자료

Leave a comment