배경지식 쌓기 - 디자인 패턴 Observer Pattern

GoF Design Pattern

Posted by Yan on August 10, 2021

Observer Pattern

  • 관찰자 패턴
  • 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴
  • 일대다 관계
  • 상태가 업데이트되면 모든 옵저버들에게 정보를 갱신할 수 있도록 한다.
  • 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다.
  • 발행/구독 모델로 알려져있다.
  • 옵저버 패턴에서 주재자는 옵저버에 대한 정보가 없다. 오직 옵저버가 특정 인터페이스를 구현한다는 것만 알고 있다. 옵저버가 무슨 동작을 하는지 모른다.
  • 옵저버는 언제든지 새로 추가되거나 제거될 수 있으며 새로운 형식의 옵저버를 추가할 때도 subject에 전혀 영향을 주지 않는다 -> 느슨한 결합

옵저버 패턴 구조(UML 다이어그램)

  • 옵저버(리스너)라 불리는 하나 이상의 객체를 관찰 대상이 되는 객체에 등록시킨다.
  • 각각의 옵저버들은 관찰 대상인 객체가 발생시키는 이벤트를 받아 처리한다.
  • Subject : 관찰 대상인 객체(이벤트를 발생시키는 주체)
  1. 이벤트가 발생하면 각 옵저버는 콜백을 받는다. notify()는 관찰 대상이 발행한 메세지 이외에 옵저버 자신이 생성한 인자값을 전달할 수 있다.
  2. 각각 파생 옵저버는 Observer인터페이스의 notify()를 구현하여 이벤트가 발생했을 대 처리할 각자의 동작을 정의해야 한다.

Subject

  • Subject에는 일반적으로 등록register, 제거unregister 메소드가 있다.
  • register : 새로운 옵저버를 목록에 등록한다.
  • unregister : 목록에서 옵저버를 뺀다.
  • 이외에도 임시로 작동을 멈추거나 재개하는 메소드를 이용해 이벤트가 계속해서 이어질 때 요청을 제어할 수도 있다.

주의점

  • 순환 실행을 막아야한다. 종종 콜백은 속성 값 변화를 처리하기 위해 호출될 뿐 아니라 속성 값 또한 바꾼다. 때때로 이벤트 연쇄의 원인이 될 수 있다.
  • 옵저버 패턴은 MVC에서 모델과 뷰 사이를 느슨히 연결하기 위해 사용된다. 대표적으로 모델에서 일어나는 이벤트를 통보받는 옵저버는 뷰의 내용을 바꾸는 스위치를 작동시킨다.
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
/* 파일명 : EventSource.java */

package obs;
import java.util.Observable; // 이 부분이 옵저버에게 신호를 보내는 주체.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class EventSource extends Observable implements Runnable
{
    public void run()
    {
        try
        {
            final InputStreamReader isr = new InputStreamReader( System.in );
            final BufferedReader br = new BufferedReader( isr );
            while( true )
            {
                final String response = br.readLine();
                setChanged();
                notifyObservers( response );
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 파일명: ResponseHandler.java */

package obs;

import java.util.Observable;
import java.util.Observer; /* 여기가 옵저버 */

public class ResponseHandler implements Observer
{
    private String resp;
    public void update (Observable obj, Object arg)
    {
        if (arg instanceof String)
        {
            resp = (String) arg;
            System.out.println("\nReceived Response: "+ resp );
        }
    }
}
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
/* 파일명 : myapp.java */
/* 여기서부터가 프로그램 시작점 */

package obs;

public class MyApp
{
    public static void main(String args[])
    {
        System.out.println("Enter Text >");

        // 이벤트 발행 주체를 생성함 - stdin으로부터 문자열을 입력받음
        final EventSource evSrc = new EventSource();

        // 옵저버를 생성함
        final ResponseHandler respHandler = new ResponseHandler();

        // 옵저버가 발행 주체가 발행하는 이벤트를 구독하게 함
        evSrc.addObserver( respHandler );

        // 이벤트를 발행시키는 쓰레드 시작
        Thread thread = new Thread(evSrc);
        thread.start();
    }
}

reference

옵서버 패턴