1. 옵저버 패턴의 정의
한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의함. 옵저버 패턴은 실제 구현할 수도, 자바 내장 패턴을 이용할 수도 있다.
옵저버 패턴은 상태를 저장하고 있는 주제 객체와, 상태가 변하는것을 바라보고 있는 옵저버들로 이루어져 있다.
옵저버 패턴의 구조
Subject interface
public interface Subject {
public void registerObserver(Observer observer); // 등록하기 위한 observer를 인자로 받는다.
public void deleteObserver(Observer observer); // 삭제하기 위한 observer를 인자로 받는다.
public void notifyObserver(); // subject 객체의 상태(소식)이 변경되었을 때 모든 observer들에게 알리 위해 호출되는 메소드
}
Observer interface
public interface Observer {
public void update(float temp, float humidity, float pressure); // 상태가 갱신되었을 때 전달할 정보들을 인자로 받는다.
}
DisplayElement interface
public interface DisplayElement
public void display();
}
Subject 구현 클래스
import java.util.ArrayList;
public class WeatherData implements Subject {
// field
private float temp; // // 느슨한 결합(Loose Coupling)을 위해 상위형식으로 구성을 사용
private float humidity;
private float pressure;
// constructor
public WeatherData() {
private ArrayList<Observer> observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void deleteObserver(Observer observer) {
int i = observers.indexOf(observer);
if(i>=0) {
observers.remove(i);
}
}
// subject객체가 가진 목록 즉, 모든 observer들의 update() 호출
@Override
public void notifyObserver() {
for(int i = 0 ; i<observers.size() ; i++ ){
Observer observer = observers.get(i);
observer.update(this.temp, this.humidity, this.pressure);
}
}
// subject의 상태(소식)이 변경되었을 때 호출 : observer들에게 상태를 알림
public void measurementsChanged() {
notifyObserver();
}
// subject 객체의 상태를 갱신
public void setMeasurements(float temp, float humidity, float pressure) { //
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
Observer 구현 클래스
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temp;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionDisplay(Subject weatherData) {
this.weatherData = weatherData;
this.weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("Current Condition: "+this.temp+"F degrees and "+this.humidity+"% humidity and "+pressure+" pressure");
}
}
Test 클래스
public class WeatherStation {
public static void main(String args[]) {
WeatherData weatherData = new WeatherData(); // 서브젝트 객체 생성과 동시에 리스트 생성됨
CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay(weatherData); //옵저버 클래스 생성하면서 등록됨.
weatherData.setMeasurements(32, 65, 30); 값을 설정하게 되면 옵저버 update 메소드를 호출함
}
}
앞서 나온 방식을 이용하게되면 3가지 정보를 모두 넘겨주지만 실제 옵저버 클래스에서는 temp와 humidity만 사용하는데 모든 정보를 넘겨받는다는 점과, 정보를 받고싶은 상황을 선택하고 싶을 경우에는 어떻게 구현해야할까?
observer.update(this.temp, this.humidity, this.pressure);
2. 옵저버 패턴의 push vs pull
앞서 나온 정보가 변경될 경우 주제에서 옵저버에게 변경사항을 전달해주는 방식을 push방식이라고 한다. 이와 다르게 옵저버가 필요할 때마다 정보를 요청하는 방식을 pull방법이라 한다. JAVA에서 제공하는 옵저버 클래스를 활용하여 pull방식을 구현해보자.
Java 내장 옵저버를 이용하면 다음과 같다. Observable은 인터페이스가 아닌 클래스이고 앞서만든 Subject interface와 같은 역할을 한다.
Observable 클래스를 상속받은 WeatherData
import java.util.ArrayList;
public class WeatherData extends Observable {
private float temp; // // 느슨한 결합(Loose Coupling)을 위해 상위형식으로 구성을 사용
private float humidity;
private float pressure;
public WeatherData() { } // Array를 사용할 필요 X
// subject의 상태(소식)이 변경되었을 때 호출 : observer들에게 상태를 알림
public void measurementsChanged() {
setChanged();
notifyObservers();
}
public void setMeasurements(float temp, float humidity, float pressure) { //
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature(){
return temperature;
}
public float getHumuduty(){
return humuduty;
}
public float getPressure(){
return pressure;
}
}
Observer 구현 클래스
public class GeneralDisplay implements Observer, DisplayElement {
Observable observable;
private float temp;
private float humidity;
public CurrentConditionDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
public void update(Observable obs, Object arg) { //값을 받는것이 아닌 객체를 통해 get메소드로 원하는 정보를 가져간다.
if(obs instaceof WeatherData){
WeatherData weatherData = (WeatherData)obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
public void display() {
System.out.println("Current Condition: "+this.temp+"F degrees and "+this.humidity+"% humidity and "+pressure+" pressure");
}
}
pull 방식은 다음과 push방법처럼 정보를 주입 받는것이 아니라 다음과 같이 객체를 받아 원하는 정보를 가져간다는 특징이 있다.
public void update(Observable obs, Object arg) { //값을 받는것이 아닌 객체를 통해 get메소드로 원하는 정보를 가져간다.
setChanged()는 무엇을 의미할까? Observable 슈퍼 클래스에 정의된 flag이다. 즉 변화로 인정해서 update할지 말지 의미하는것이고 내가 온도변화를 3도 이상 변했을경우만 setChange를 할수 있게 변경할 수 있다.
3. 옵저버 패턴의 예시
MVC패턴에서 옵저버 패턴을 찾아볼 수 있다. 옵저버 패턴이란 상태변화를 통지해 주는것을 의미했는데 MVC패턴은 다음과같다.
- 컨트롤러에 의해 Model의 상태가 변함 (Model의 값이 변함)
- 뷰는 모델에 값이 변할때 상태 변화를 모델로부터 직접 가져옴(Pull)
'프로그래밍 > 시큐어코딩' 카테고리의 다른 글
[디자인 패턴] 커맨드 패턴 (0) | 2020.07.13 |
---|---|
[디자인 패턴] 팩토리 메소드 패턴 (0) | 2020.07.13 |
[스프링 이해와 원리] 프록시패턴, 데코레이터패턴이란? (0) | 2020.07.09 |
[스프링 이해와 원리] AOP란? (0) | 2020.07.09 |
[스프링 이해와 원리] 서비스단에서 트랜잭션 추상화하기 (0) | 2020.07.08 |