옛글/프로그래밍이야기

프로그래밍 Design Pattern 이해하기 - 2 옵저버 패턴

ShakeJ 2014. 7. 30. 20:48
반응형

옵저버 패턴



옵저버 패턴(Observer Pattern)은 한 객체의 상태가 바뀌면, 그 객체에 의존하는 다른 객체들이 자동으로 갱신되는 일:다 패턴이다.


'한 객체의 상태가 바뀌면, 그 객체가 의존하는 다른객체들(옵저버)에게 연락이 간다'

'출판사가 주체라면, 구독자는 옵저버가 된다'


옵저버 패턴의 조건


옵저버 패턴에서 상태를 저장하고 있는 것은 주체다. 


옵저버는 이 '상태'를 사용하고 있지만, '반드시' 가지고 있어야 하는 것은 아니다. 

최대한 느슨하고 또 느슨하게 결합을 가지도록 노력해야 하낟. 


객체간의 결합도가 높을 수록 유지보수가 힘든 작업이 될 수 밖에 없다. 

1편에서 봤던 '스트레티지 패턴'을 보면 '상속'보다는 '위임(인터페이스)'을 쓰면서 메소드만 가진 객체를 분리했듯이! 어떤 패턴이든 결합도를 낮추는 것이 중요하다. 결합도를 낮출수록 확장성이 용이하기 때문이다. 




이를테면, '통합 기상정보 시스템'을 만든다고 생각해보자.


습도센서, 온도센서, 압력센서에서 가져온 데이터들을 'Station'에서 종합하고 이를 Weather Data 객체로 만든다. (Weather Data는 옵저버 1) 그리고 표시되는 디스플레이들(매우 많은 '다') 즉 1:다 관계가 성립하게 된다. 


 Weather Data 객체가 '주제 객체'가 된다.


1번 디스플레이는 '온도', '습도', '압력'을 보여주고, 2번 디스플레이는 '날씨', 3번 디스플레이는 '기온'을 표시한다고 했을 때, 

장치들은 자기가 원하는 정보를 얻기 위해서, WeatherData 객체에 등록을 할 수 있는 메서드를 필요로 한다. 


동작시나리오를 살펴보면,


1. WeatherStation에서는 온도, 습도, 기압정보를 수집한다.

2. WeatherStation에서 주기적으로 30분에 한번씩 현재 데이터를 WeatherData에 준다.

3. WeatherData는 새데이터를 받으면, Observable에 Observer로 등록되어 있는 다양한 기기들에게 새로운 데이터를 전달한다. 

4. 디바이스들은, 새로운 데이터들을 표시한다.

반복한다!


Java에서 이를 구현하기 위해서는 두가지 방법이 있다.


- JDK에서 지원하는 Observable, Observer API를 활용한다. 

- 직접 구현한다.


Java.util에 있는 Obersavable 클래스와 Observer 인터페이스는 자바 내장 API이다. Observable클래스는 등록된 옵저버를 관리하고, 새로운 데이터가 들어오면 옵저버에게 데이터를 전달한다. 옵저버 클래스들은 Observer인터페이스를 implements해서 데이터를 받을 수 있다. 

하지만, 다른 인터페이스를 implement하고 있는 경우에 중복으로 상속이 불가능하기 때문에 확장성에 불리하다. 


java.util.Observable.class


- addObserver(Observer o) : 옵저버를 등록한다. 이후에 들어오는 데이터는 등록된 옵저버에 전달된다.

- deleteObserver(Observer o) : 옵저버를 제거한다. 이후에 들어오는 데이터는 해당 옵저버에 전달되지 않는다.

- notifyObservers(), notifyObservers(Object arg) : 새로운 데이터가 들어오면 등록된 옵저버에 새로운 데이터와, 파라미터를 전달한다.

- deleteObservers() : 등록된 모든 옵저버를 제거한다. 이후에 들어오는 데이터는 전달되지 않는다.

- setChanged() : 신규 데이터가 들어오면, changed 값을 true로 변경, changed 변수가 true 일때만 데이터가 옵저버에 전달된다.

- clearChanged() : changed 값을 false 로 변경한다. 신규 데이터를 옵저버에 전달이 완료되면 clearChanged()를 호출한다.

- hasChanged() : 현재 changed 의 값을 반환한다.

- countObservers() : 현재 등록되어있는 옵저버의 수를 반환한다.


Observer.class


- update(Observable o , Object arg) : Observable에게 전달받은 새로운 데이터를 갱신한다.


예를 들면,




[음... 알아서 한 건 아니지만 MeetThere에서 SocketController에다 Listener들을 등록해서 ArrayList로 가지고 있고, 소켓이 변화하면 모든 Listener들에게 변화가 발생하는 것이 옵저버패턴이다]


유저들이 채팅을 하고있고, 소켓을 통해 돌아다니는 정보들을 UserList와 Map에 소켓에 업데이트 되는 정보들을 Display한다. 


Socket Controller는 1이며, UI컴포넌트들은 '다', 1:다 관계를 가지고 있으며, 소켓컨트롤러는 Listener들을 가지고 있으며, 정보가 업데이트 되는 순간 나머지 컴포넌트들이 업데이트 된다. 


[보충내용]


 그렇다면, 옵저버패턴의 단점아닌 단점은 무엇일까?


 너무 편리하고, 어느 곳에든 쓸 수 있다는 점 때문에 굉장히 밀접하고 동기식으로 이루어져야 하는 관계 간에도 옵저버패턴을 남용할 수 있다는 점. 굉장히 중요한 정보를 변경하고 값을 다시 받아 체크해야 하는 경우에도, 브로드캐스트를 하듯이 '바꼈으니 쓰시오~' 라고 던져놓으면, 옵저버 클래스들이 '어 바꼈네?' 하는 옵저버패턴을 사용하는 소스가 종종 있다는 점!



반응형