옵저버 패턴(Observer Pattern) (2/3) - Observer 패턴 직접 구현옵저버 패턴(Observer Pattern) (2/3) - Observer 패턴 직접 구현

Posted at 2013. 11. 27. 14:25 | Posted in == JAVA ==/Design Patterns



facebook에 글올리기



옵저버 패턴 (Observer Pattern) (2/3)



옵저버 패턴(Observer Pattern)(1/3) - java.util.Observable 에 이어서 포스팅하겠다.

앞의 포스팅을 보고 오는게 옵저버 패턴을 이해하는데 더 도움이 될 것이다.


3. Observable, Observer 직접 구현하여 Observer 패턴 구현


앞에서 말했듯이 확장과 재사용성을 위해 클래스로 되어있던 Observable을 인터페이스로 만들어 옵저버와 느슨한 결합으로 구현하겠다.


(1) 인터페이스의 느슨한 결합


두 객체가 느슨하게 결합되어 있다는 것은, 그 둘이 상호작용을 하긴 하지만 서로에 대해 서로 잘 모른다는 것을 의미한다. 

- Observable은 Observer의 구상 클래스가 무엇인지, 옵저버가 무엇을 하는지 등에 대해서는 알 필요가 없다.

- 옵저버는 언제든지 새로 추가할 수 있다. 새로운 형식의 옵저버를 추가하려고 할 때도 Observable을 전혀 변경할 필요가 없다.

- Observable과 Observer는 서로 독립적으로 재사용할 수 있다.

- Observable이나 Observer가 바뀌더라도 서로한테 영향을 미치지는 않는다.


느슨하게 결합하는 디자인을 사용하면 변경 사항이 생겨도 무난히 처리할 수 있는 유연한 객체지향 시스템을 구축할 수 있다.




(2) 옵저버 패턴 UML 구조


인터페이스로 직접 구현한 Observable과 Observer 를 사용한 옵저버패턴 UML 구조다.




앞에 java.util API를 활용한 구성과의 차이점을 찾을 수 있겠는가?

가장 큰 차이점은 Observable이 인터페이스가 되어 WeatherData가 Implements 하면서 함수들을 구현한 것이다.


Observable과 그것을 구성하는 WeatherData를 제외하고 다른 부분은 바뀌는 부분이 없다. 


동작원리 또한 같다. 여기에서는 옵저버 등록(addObserver()), 옵저버 제거(deleteObserver()), 옵저버에게 데이터 전달(notifyObserver()) 만 구현했다. (setChanged() 등 필요한 부분은 직접 추가해서 구현하면 된다.)


(3) Observable.java (인터페이스)


1
2
3
4
5
6
7
8
public interface Observerable {
    
    public void addObserver(Observer o);             // 옵저버 등록
    public void deleteObserver(Observer o);          // 옵저버 제거
    public void notifyObservers();                         // 옵저버에 데이터 전달

}


Observable 인터페이스는 간단하다. 이제 WeatherData에서 이 Observable을 implements 해서 옵저버 등록, 제거, 데이터전달의 기능을 구현해야 한다.


(4) WeatherData.java (인터페이스)


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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

import java.util.ArrayList;

public class WeatherData implements Observable{            // Observable 인터페이스 implements
    
    private ArrayList<Observer> observers;            // 등록할 옵저버 리스트
    private float temperature;                        // 온도
    private float humidity;                            // 습도
    private float pressure;                            // 기압
    
    public WeatherData(){                            // 생성자
        observers = new ArrayList<Observer>();        // 옵저버를 등록할 수 있는 ArrayList 생성
    }
    
    // 옵저버 등록
    public void addObserver(Observer o){            
        observers.add(o);
    }
    
    // 옵저버 제거
    public void deleteObserver(Observer o){            
        int i = observers.indexOf(o);                
        if(i>=0){
            observers.remove(i);
        }
    }
    
    // 옵저버에 데이터 전달
    public void notifyObservers(){                            
        for(int i=0; i<observers.size(); i++){
            Observer observer = (Observer)observers.get(i);
            observer.update(this);    // update로 옵저버에게 전달한다.
        }
    }
    
    // 최신 데이터 갱신,  기상 스테이션에서 이 메소드로 최신의 데이터를 던져준다.
    public void setMeasurements(float temperature, float humidity, float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
    
    // 데이터 변경 발생
    public void measurementsChanged(){
        notifyObservers();                // 데이터 변경 발생시 옵저버들에게 데이터를 전달한다.
    }
    

    // getter, setter 구현

   //  getter, setter 생략....  소스코드 확인
}


- WeatherData()  :  WeatherData 의 생성자 함수로 옵저버를 등록할 ArrayList를 생성한다.

- addObserver(Observer o) : 해당 옵저버를 옵저버로 등록한다. 이후에 전달받은 데이터는 해당 옵저버에 전달된다.

- deleteObserver(Observer o) : 해당 옵저버를 옵저버 리스트에서 제거한다. 이후에 전달받은 데이터는 해당 옵저버에 전달되지 않는다

- notifyObservers() : 데이터를 등록된 옵저버들에게 전달한다.

- setMeasurements(float temperature, float humidity, float pressure) : 최신 데이터를 갱신한다. 기상 스테이션에서 이 메소드로 최신의 데이터를 던져준다.

- measurementsChanged() : 데이터가 변했을 때 작동한다.  옵저버들에게 새로운 데이터를 던지는 notifyObservers()를 호출한다.


앞에서 보았던 java.util.Observable로 구현한 옵저버와 비슷할 것이다. 값이 변했을때, 상태값을 설정해주는 setChanged() 및 현재 등록 옵저버의 개수를 확인하는 countObservers() 등은 구현하지 않았지만, 직접 구현하기 때문에 필요하다면 얼마든지 추가할 수 있다.

옵저버 패턴(Observer Pattern)(1/3) - java.util.Observable 을 잘 보았다면 어려운 부분은 없을 것이다.





(5) 소스코드 실행하기


Observer.java, DisplayElement.java, CurrentConditionDisplay.java, ForecastDisplay.java, WeatherStation.java 모두 바뀌는 부분은 없다. 기존의 코드를 그대로 사용해도 잘 동작한다.


(혹시 앞의 포스트를 잘 읽어본 사람은 눈치챘을지도 모른다.

앞의 포스트에서의 실행 결과의 순서가 뒤바꼈다.

앞에서는 Forecast가 먼저 출력되었지만, 이번에는 CurrentCondition이 먼저 출력됐다. 이것은 Observer를 저장하는 형(Type)의 차이다. java.util.Observable에서는 등록된 옵저버를 Vector에 저장한 후, notify시 순서대로 던져준다.

반면, 여기서 Observable 인터페이스를 구현한 WeatherData에서는 ArrayList로 저장하여, 순서대로 던져준다. 형(Type)의 차이로 꺼내는 순서가 바뀐것이다. 순서가 중요하다면 java.util.Obsrvable 을 상속해서 구현할때는 그대로 따라가야되지만, Observable 인터페이스를 구현할때는, 원하는 방식으로 구현하면 된다.)


java.util.Observable을 Observable.java 인터페이스로, 바꾸고 상속받았던 WeatherData를 새로 만든 Observable.java 인터페이스를 implements 해서 구현한 부분만 변경되었다.


이어서 다음 포스트에 위의 옵저버패턴을 변경/확장해 보도록 하겠다.


(6) 옵저버 패턴 구현 방법의 선택


JAVA 내장 API java.util.Observable 패턴을 사용한 구현과 Observable 인터페이스를 만들어서 직접 구현한 방법 두가지를 살펴보았다.


어떤 방법이 더 좋은 방법일까?


정답은 없다. 그때 구현 목적, 향후 확장 및 변경을 고려하여 최선의 방법을 선택하는게 좋다.

java.util.Observable 패턴을 사용하면, 구현이 편하다. Observable을 상속만 해주면 되기 때문이다.

하지만 상속을 사용함으로써, 확장과 재사용성이 떨어지는 것이 사실이다.

Observer 패턴 구현이 어려운 것은 아니기 때문에 직접 구현하는 방법도 좋다. 특히, 인터페이스를 구현하면서 확장과 재사용성에 열려있다.


어떤 방법을 사용하냐는 본인들의 몫이다.



이어서 다음 포스트에 위의 옵저버패턴을 변경/확장해 보도록 하겠다.



옵저버 패턴(Observer Pattern)(1/3) - java.util.Observable


옵저버 패턴(Observer Pattern) (2/3) - Observer 패턴 직접 구현


옵저버 패턴(Observer Pattern) (3/3) - Observer 패턴 변경 확장



이웃추가
facebook에 글올리기

Name __

Password __

Link (Your Website)

Comment

SECRET | 비밀글로 남기기