본문 바로가기
2023년 이전/디자인 패턴

디자인 패터 - 옵저버 패턴(Observer Pattern)

by JeongUPark 2019. 11. 18.
반응형

옵서버 패턴(observer pattern)은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴입니다. 주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용됩니다.

한객체의 상태가바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로

일대다(one-to-many) 의존성을 정의합니다.

옵저버 패턴의 그 구성을 보면 위의 다이어그램과 같습니다.

Oserver - 데이터 변경을 통보 받는 인터페이스. 즉 Subject에서 Observer 인터페이스의 update를 호출하여 ConcreateSubject의 데이터 변경을 ConcreteObserver에게 통보

Subject - ConcreateObserver를 관리.

ConcreateSubject - 변경 관리 대상이 되는 데이터가 있는 클래스(통보하는 클래스). 데이터 변경 시 subject의 notifyObserver를 호출하여 ConcreateObserver 객체에 변경을 통보

ConcreteObserver - ConcreateSubject 를 통하여 통보받는 클래스 Observer 인터페이스의 update를 구현함으로써 변경을 통보받는다.

 

옵저버 패턴을 구현하는 방법에는 여러가지가 있지만 대부분 상태를 저장하고있는 주제 인터페이스를 구현한 하나의 주제객체와 주제객체에 의존하고있는 옵저버 인터페이스를 구현한 여러개의 옵저버객체가 있는 디자인을 바탕으로 합니다. 데이터의 변경이 발생했을 경우 상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 유용합니다.

주제객체에서 옵저버로 데이터를 보내는 방식(푸시 방식)

옵저버에서 주제객체의 데이터를 가져가는 방식 (풀 방식)


다음은 옵저버 패턴의 예시 입니다. 

우선 Observer들을 등록하고 관리하는 subject를 만듭니다.

import java.util.ArrayList;
import java.util.List;

public abstract class Subject {

    private List<Observer> observers = new ArrayList<Observer>();

    public void registerObserver(Observer observer){
        observers.add(observer);
    }

    public void removeObserver(Observer observer){
        observers.remove(observer);
    }

    public void notificationObserver(){
        for(Observer o : observers){
            o.update();
        }
    }
}

 그리고 subejct를 상속받는 ConcreateSubject를 만들어서 어떤 변화가 있을 경우 처리 후 notifyObserver를 호출 합니다.(여기서는 notificationObserver() 입니다.)

public class Center_Observer extends Subject {

    public void findMap(){

        System.out.println("Center Observer move finish");
        notificationObserver();
    }
}

Observer interface를 만들고

public interface Observer {
    public abstract void update();
}

Observer interface를 상속받는 ConcreateObserver를 만듭니다. subject에서 데이터 변경에 의한 noti가 발생할 경우 각 ConcreateObserver가 받아서 그에 따른 update 사항을 처리합니다.

public class move_Observer implements Observer{

    private int mNum;
    move_Observer(int num){
        mNum = num;
    }
    @Override
    public void update() {
        System.out.println(mNum +" also catch");
    }
}

 그리고 client에서 다음과 같이 사용하면 됩니다.

public class Client {

    public static void main(String[] args){


        Center_Observer c_Observer = new Center_Observer();
        move_Observer m_Observe_1 = new move_Observer(1);
        move_Observer m_Observe_2 = new move_Observer(2);
        move_Observer m_Observe_3 = new move_Observer(3);
        c_Observer.registerObserver(m_Observe_1);
        c_Observer.registerObserver(m_Observe_2);
        c_Observer.registerObserver(m_Observe_3);


        c_Observer.findMap();
    }
}

 즉 위의 code는 ConcreateSuject에서 데이터 변경 후 subject의 notificationObserver를 호출 하여 그 변경 사항을 ConcreteObserver들에게 알리면 각 concreateObserver들의(위에서는 move_Observer 들) update로 통보하여 그 부분에서 처리하게 됩니다.

 

그리고 옵저버 패턴의 위의 예제처럼 직접 만들수 있지만 java에서 제공하는 API를 통해서도 만들 수 있습니다.

import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.util.*

class EventSource() : Observable(), Runnable {

    override fun run() {
        try
        {
            val  isr = InputStreamReader( System.`in` )
            val  br = BufferedReader( isr );
            while( true )
            {
                val response = br.readLine();
                setChanged();
                notifyObservers( response );
            }
        }
        catch ( e: IOException)
        {
            e.printStackTrace();
        }
    }
}
import java.util.*
import java.util.Observer

class ResponseHandler  : Observer {

    var resp : String? = null
    override fun update(p0: Observable?, p1: Any?) {

        if(p1 is String){
            resp = p1 as String
            println("Received Response: $resp")
        }
    }

}
fun main(){
    val evSrc = EventSource()
    val respHandler = ResponseHandler()

    evSrc.addObserver(respHandler)
    val thread = Thread(evSrc)
    thread.start()
}

그런대 java.util.Observer가 JAVA 9부터 안쓰인다고 하여, 알아봤더니 java.beans.PropertyChangeEvent 와 

java.beans.PropertyChangeListener를 사용하면된다고 해서 동일한 동작을 다시 구현해 보았습니다.

import java.beans.PropertyChangeEvent
import java.beans.PropertyChangeListener

class Response_Handler : PropertyChangeListener{
    var resp : String? = null
    override fun propertyChange(p0: PropertyChangeEvent?) {

        if(p0 is PropertyChangeEvent){

            resp =  p0.newValue.toString()
            println("property event Response: $resp")
        }
    }

}
import java.beans.PropertyChangeEvent
import java.beans.PropertyChangeListener
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader

class Event_Source : Runnable {
    private var listeners = mutableListOf<PropertyChangeListener>()


    fun addListener(propertyChangeListener: PropertyChangeListener){
        listeners.add(propertyChangeListener)
    }
    override fun run() {
        try
        {
            val  isr = InputStreamReader( System.`in` )
            val  br = BufferedReader( isr );
            while( true )
            {
                val response = br.readLine();

                for( o in listeners){
                    var p_event = PropertyChangeEvent(this,"input",response,"input: $response");
                    o.propertyChange(p_event)
                }
            }
        }
        catch ( e: IOException)
        {
            e.printStackTrace();
        }
    }

}
fun main(){
    val ev_src = Event_Source()
    val resp_handler = Response_Handler()
    ev_src.addListener(resp_handler)
    val thread = Thread(ev_src)
    thread.start()
}

 

이 Observer pattern은 Rx프로그래밍에서 중요한 개념으로 이 패턴을 잘 기억하면 Rx 프로그래밍 공부시 도움이 많이 될것 같습니다.

 

 

참고

https://ko.wikipedia.org/wiki/%EC%98%B5%EC%84%9C%EB%B2%84_%ED%8C%A8%ED%84%B4#%EB%8C%80%ED%91%9C%EC%A0%81%EC%9D%B8_%EC%82%AC%EB%A1%80

https://jusungpark.tistory.com/8?category=630296

https://gmlwjd9405.github.io/2018/07/08/observer-pattern.html

 

반응형