옵저버 패턴(Observer Pattern)

디자인패턴 2014. 11. 11. 14:22

1. 옵저버 패턴이란?


 옵저버 패턴(Observer Pattern)에서는 한 객체(Subject)의 상태가 바뀌면 그 객체에 의존하는 다른 객체들(Observer)한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의한다.



2. 옵저버 패턴 예시


(1) 소스 코드

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
public interface Subject {
 // 옵저버를 관리하기 위한 인터페이스
 public void registerObserver(Observer o);
 public void removeObserver(Observer o);
 // 등록된 옵저버들에게 데이터를 전달
 public void notifyObservers();
}
 
 
public interface Observer {
 // Subject가 보낸 자료를 처리
 public void update(float temp, float humidity, float pressure);
}
 
 
public interface DisplayElement {
 public void display();
}
 
 
// 기상 정보를 옵저버들에게 나누어 줄 것이므로, WeatherData가 Subject가 된다.
// 그러므로, Subject 인터페이스를 implements 한다.
public class WeatherData implements Subject{
 private ArrayList<observer> observers;
 private float temperature;
 private float humidity;
 private float pressure;
 
  
 public WeatherData() {
  observers = new ArrayList<observer>();
 }
  
 
 @Override
 public void registerObserver(Observer o) {
  observers.add(o);
 }
 
 
 @Override
 public void removeObserver(Observer o) {
  int i = observers.indexOf(o);
  if (i >= 0) {
   observers.remove(i);
  }
 }
 
 
 @Override
 public void notifyObservers() {
  for (int i = 0; i < observers.size(); i++) {
   Observer observer = (Observer)observers.get(i);
   observer.update(getTemperature(), getHumidity(), getPressure());
  }
 }
  
 
 public void measurementsChanged() {
  this.notifyObservers();
 }
  
 
 public void setMeasurements(float temperature, float humidity, float pressure) {
  // 기상정보 측정 값이 변경되면 measurementsChanged 메소드가 호출되고,
  // 이어서 notifyObservers 메소드를 통해 옵저버들에게 기상정보가 보내진다.
  this.temperature = temperature;
  this.humidity = humidity;
  this.pressure = pressure;
  this.measurementsChanged();
 }
 
 
 private float getTemperature() {
  return temperature;
 }
 
 
 private float getHumidity() {
  return humidity;
 }
 
 
 private float getPressure() {
  return pressure;
 }
}
 
 
// 현재 기상 상태를 표시하는 장치이므로, 기상 정보를 구독하는 Observer가 된다.
// 그러므로 Observer 인터페이스를 implements 한다.
public class CurrentConditionDisplay implements Observer, DisplayElement {
 private float temperature;
 private float humidity;
 private Subject weatherData;
  
 
 public CurrentConditionDisplay(Subject weatherData) {
  // 현재 기상 상태 표시하는 장치 생성과 동시에 WeatherData 클래스에 옵저버로 등록한다.
  this.weatherData = weatherData;
  weatherData.registerObserver(this);
 }
 
 
 @Override
 public void display() {
  System.out.println("Current conditions : " + temperature + "F degrees and " + humidity + "% humidity");
 }
 
 
 @Override
 public void update(float temperature, float humidity, float pressure) {
  // Subject로부터 받은 데이터를 update 메소드에서 처리한다.
  this.temperature = temperature;
  this.humidity = humidity;
  this.display();
 }
 
}
 
 
// 옵저버 패턴을 적용한 가상 기상 정보 시스템이다.
public class WeatherStation {
 
 
 /**
 
  * @param args
 
  */
 public static void main(String[] args) {
  // Subject 객체를 생성한다.
  WeatherData weatherData = new WeatherData();
   
  // WeatherData 클래스의 옵저버로 등록을 한다.
  CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(weatherData);
  HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);
  weatherData.setMeasurements(80, 65, 30.4f);
 }
 
}
</observer></observer>


(2) 결과






3. 자바 내장 라이브러리를 이용한 옵저버 패턴 예시


(1) 소스 코드

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// java.util.Observable 클래스를 상속받아 Subject를 구현한다.
public class WeatherData extends Observable {
 private float temperature;
 private float humidity;
 private float pressure;
  
 public WeatherData() {
  // TODO Auto-generated constructor stub
 }
  
 public void measurementsChanged() {
  // setChanged 메소드는 데이터가 변경되었다는 것을 밝히기 위한 메소드로 변경되었다면 true, 그렇지 않다면 false를 리턴한다.
  // setChanged 메소드를 통해 옵저버들에게 데이터를 전달하기 위한 조건을 줘서 필요할 때만 전달되어지도록 제한을 할 수 있다.
  setChanged();
  notifyObservers();
 }
  
 public void setMeasurements(float temperature, float humidity, float pressure) {
  this.temperature = temperature;
  this.humidity = humidity;
  this.pressure = pressure;
  measurementsChanged();
 }
  
 public float getTemperature() {
  return temperature;
 }
  
 public float getHumidity() {
  return humidity;
 }
  
 public float getPressure() {
  return pressure;
 }
}
 
 
public interface DisplayElement {
 public void display();
}
 
 
public class CurrentConditionsDisplay implements Observer, DisplayElement{
 Observable observable;
 private float temperature;
 private float humidity;
  
 public CurrentConditionsDisplay(Observable observable) {
  this.observable = observable;
  observable.addObserver(this);
 }
  
 @Override
 public void display() {
  System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
 }
 
 @Override
 public void update(Observable o, Object arg) {
  if (o instanceof WeatherData) {
   WeatherData weahterData = (WeatherData)o;
   this.temperature = weahterData.getTemperature();
   this.humidity = weahterData.getHumidity();
   display();
  }
 }
 
}
 
 
public class WeatherStation {
 
 /**
  * @param args
  */
 public static void main(String[] args) {
  WeatherData weatherData = new WeatherData();
   
  CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
 }
 
}


(2) 결과






4. java.util.Observable의 단점


(1) Observable은 클래스이다.
1) Observable이 클래스기 때문에 서브 클래스를 만들어야 한다는 점이 문제가 된다. 이미 다른 Superclass를 상속하고 있는 클래스는 Observable 클래스를 상속할 수 없기 때문이다. 그래서 재사용성에 제약이 생기게 된다.
2) Observable 인터페이스라는 것이 없기 때문에 자바에 내장된 Observer API하고 잘 맞는 클래스를 직접 구현하는 것이 불가능하다. java.util 구현을 멀티스레드로 구현한다거나 하는 일이 불가능하다.

(2) Observable 클래스의 핵심 메소드를 외부에서 호출할 수 없다.
 Observable API에서 setChanged 메소드가 protected로 선언되어 있다. 그러므로, Observable을 상속한 Subclass에서만 setChanged 메소드를 호출할 수 있다. 결국 어떤 클래스를 만들고, Observable의 Subclass를 Instance 변수로 사용하는 방법을 쓸 수가 없다.




출처 - http://digitanomad.blogspot.kr/2013/01/observer-pattern.html

'디자인패턴' 카테고리의 다른 글

싱글턴 패턴(Singleton Pattern)  (0) 2014.11.11
팩토리 패턴(Factory Pattern)  (0) 2014.11.11
데코레이터 패턴(Decorator Pattern)  (0) 2014.11.11
어댑터 패턴(Adapter Pattern)  (0) 2014.11.11
싱글톤 패턴  (0) 2012.11.02
: