본문 바로가기
소프트웨어공학/디자인 패턴

Observer Pattern 옵저버 패턴 code , java built-in package | Design pattern 디자인 패턴

by javapp 자바앱 2021. 11. 20.
728x90

 

 

 

 

Java에는 기본 지원 기능이 있습니다. 여러 API에 포함되어 있다.

 

일반적으로 java.until package에 The Observer interface and The Observable class 가 있다.

Subject and Observer interface 와 비슷하다.

 

 

 

 

The Observable class keeps track of all yours observers

and notifies them for you

 

 

 

 

 

 

 

 

 

 

This should look familiar. In fact, it's exactly the same as our previous class diagram.

 

 

 

 

How Java’s built-in Observer Pattern works

For an Object to become an observer...
  implement the Observer interface (this time the java.util.Observer interface)

For the Observable(Subject) to send notifications...
  First of all you need to be Observable by extending the java.util.Observable superclass.

   1.  You first must call the setChanged() method to signify(나타내다.) that the state has changed in your object
   2.  Then, call one of two notifyObservers() methods:
notifyObservers() or notifyObservers(Object arg)

 

For an Observer to receive notifications

# update(Subject s , Data data)
update(Observable o , Object arg)


# Observable o : 통지를 보낸 Subject는 Observable 파라미터를 통해 전달 된다.
# Object arg : notifyObservers()에 전달된다.


 


 

 


왜 setChanged() 메소드가 왜 필요한가?

 setChanged() 는 상태 변화 나타내고 notifyObservers()은 observers을 update

setChanged() {
	changed = true
}

notifyObservers(Object arg) {
	if (changed) { // the changed flag is true
		for every observer on the list {
		call update (this, arg)
	}
		changed = false
	}
}

notifyObservers() {
	notifyObservers(null)
}

 



setChanged()는 notifications. 최적화를 위해 어떻게 observers를 업데이트 할 지에 더 유연함을 준다.
The Data to send out notifications constantly



 

 

Pull model

import java.util.Observable;

//This class represents an observable object, or "data" in the model-view paradigm.
// It can be subclassed to represent an object that the application wants to have observed.
public class WeatherData extends Observable
{
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData(){}

    public void mesurementsChanged()
    {
        setChanged();           // notifyObservers() 하기 전에 상태 변화 나타낸다.
        notifyObservers();      // The PULL model, a data object 보내지 않음
    }

    public void setMeasurements(float temperature, float humidity, float pressure)
    {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        mesurementsChanged();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}

 

import java.util.Observable;
import java.util.Observer;

public class CurrentConditionsDisplay implements Observer , DisplayElement
{
    Observable observable;

    // 원하는 관심사항
    private float temperature;
    private float humidity;

    public CurrentConditionsDisplay(Observable observable)
    {
        this.observable = observable;
        observable.addObserver(this); // 자신을 subject의 Observer으로 추가
    }

    // Observer
    @Override
    public void update(Observable observable, Object object)
    {
        if (observable instanceof WeatherData)
        {
            WeatherData weatherData = (WeatherData) observable;
            this.temperature = weatherData.getTemperature(); // pulling
            this.humidity = weatherData.getHumidity();       // pulling
            display();
        }
    }

    //  DisplayElement
    @Override
    public void display() {
        System.out.println("Current conditions: " + temperature
                + "F degrees and " + humidity +"% humidity");
    }
}

 

메인문 실행

public class WeatherStation
{
    public static void main(String[] args) {
        // observable ( or subject) 생성
        WeatherData weatherData = new WeatherData();

        // observers 생성
        CurrentConditionsDisplay cc = new CurrentConditionsDisplay(weatherData);

        weatherData.setMeasurements(80,65,30.4f);
    }
}

Current conditions: 80.0F degrees and 65.0% humidity

Process finished with exit code 0

 

 

 


 

 

 

여기서 메인 실행의 흐름과 Observer , Observable 가 어떤 식으로 코드가 구성돼있는지 알아보자.

 

 

// observable ( or subject) 생성
WeatherData weatherData = new WeatherData();

우선 주제에 해당하는 데이터 객체를 생성하고

 

CurrentConditionsDisplay cc = new CurrentConditionsDisplay(weatherData);

Observer 구현 객체에 주제를 생성자를 통해 등록한다.

 

 

CurrentConditionsDisplay.java

    public CurrentConditionsDisplay(Observable observable)
    {
        this.observable = observable;
        observable.addObserver(this); // 자신을 subject의 Observer으로 추가
    }

여기서 import java.util.Observer; 의 메소드 addObserver(this) 메소드를 살펴보자

 

    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

멀티 쓰레드 환경일 경우를 대비해 synchronized 를 통해 동기적으로 동작하고 있다.

 

    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

등록 삭제하는 것도 해당한다.

 

다시 메인으로 돌아와서

        weatherData.setMeasurements(80,65,30.4f);

측정 값을 날씨데이터 객체(Subject) 설정한다.

 

 

public class WeatherData extends Observable

    public void mesurementsChanged()
    {
        setChanged();           // notifyObservers() 하기 전에 상태 변화 나타낸다.
        notifyObservers();      // The PULL model, a data object 보내지 않음
    }

Observable 의 메소드인 setChanged() 와 notifyObservers() 가 차례로 동작한다.

 

    protected synchronized void setChanged() {
        changed = true;
    }

setChanged 는 동기적으로 데이터 변화를 알려준다.

 

    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {
            /* We don't want the Observer doing callbacks into
             * arbitrary code while holding its own Monitor.
             * The code where we extract each Observable from
             * the Vector and store the state of the Observer
             * needs synchronization, but notifying observers
             * does not (should not).  The worst result of any
             * potential race-condition here is that:
             * 1) a newly-added Observer will miss a
             *   notification in progress
             * 2) a recently unregistered Observer will be
             *   wrongly notified when it doesn't care
             */
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

notifyObservers 에서 동기적으로 changed 변수를 통해 데이터 변화를 감지하고,

데이터가 변화했다면 모든 옵저들을 배열로 받는다.

 

    protected synchronized void clearChanged() {
        changed = false;
    }

clearChanged 를 통해 동기적으로 changed 를  false로 되돌린다.

 

동기문을 벗어나고 반복문을 통해 모든 옵저버에 대해 update를 시키는 것을 볼 수 있다.

 

Observer 구현 객체에서

    // PULL model.
    @Override
    public void update(Subject subject)
    {
        if(subject instanceof WeatherData)
        {
            WeatherData weatherData = (WeatherData)subject;
            this.temperature = weatherData.getTemp();       // pulling
            this.windSpeed = weatherData.getWindSpeed();    // pulling
            display();
        }
    }

update 가 실행되면 객체를 체크하고

pulling 방식으로 getter 메소드를 통해 데이터를 받고

display() 를 통해 출력한다.

 

 

 

 

 

 

 

책 출처 : Head First Design Patterns

댓글