2. Observer Pattern (옵저버 패턴)
2.1 Observer Pattern 이란?
- Subject의 상태 정보가 갱신되면 Subject에 의존하는 Observer들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한 패턴
- 느슨한 결합을 이용한 일대다 의존성 관계.
- JDK에서 가장 많이 쓰이는 패턴 중 하나.
- subject(1) : observer(多)
subject 객체에서 데이터를 관리
observer 객체들은 subject 객체에 등록되어 있으며 subject 객체의 데이터가 갱신되면 갱신 내용을 전달받는다.
2.2 기상 스테이션 APP 만들기
2.2.1 요구사항
최신 기상 정보(기온, 습도, 온도)를 수집하는 측정기부터 받은 Data를 기반으로 아래 세가지 항목을 구현 및 조건
- 현재 기상정보
- 기상 통계
- 기상 예보
- 측정기로의 최신 정보가 갱신될 때 마다 기능들이 나타내는 결과도 갱신
- 다른 개발자들이 새로운 항목을 추가할 수 있도록 확장 가능해야 함
- 사용자들이 항목을 추가/제거 가능
2.2.2 Subject interface
Observer들은 Subject에 의존한다.
Subject의 상태정보가 바뀌면 Subject를 의존하는 Observer들의 정보가 갱신된다.
| public interface Subject { public void registerObserver(Observer observer); // 등록하기 위한 observer를 인자로 받는다. public void deleteObserver(Observer observer); // 삭제하기 위한 observer를 인자로 받는다. public void notifyObserver(); // subject 객체의 상태(소식)이 변경되었을 때 모든 observer들에게 알리 위해 호출되는 메소드 } | cs |
2.2.3 Subject interface 구현 클래스
실제 Subject 역할을 할 구현 클래스이다.
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 | import java.util.ArrayList; public class WeatherData implements Subject { // field private ArrayList<Observer> observers; // observer 객체들을 저장하기 위한 목록을 가진다. private float temp; // // 느슨한 결합(Loose Coupling)을 위해 상위형식으로 구성을 사용 private float humidity; private float pressure; // constructor public WeatherData() { 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(); } } | cs |
2.2.4 Observer interface
Subject interface를 구현한 클래스를 의존할 클래스들이 구현해야하는 Observer interface이다.
| public interface Observer { public void update(float temp, float humidity, float pressure); // 상태가 갱신되었을 때 전달할 정보들을 인자로 받는다. } | cs |
2.2.5 Observer interface 구현 클래스
실제 Observer가 될 구현 클래스
해당 항목은 Subject 구현 클래스에 의존하는 Observer 목록에서 제거함 으로서 항목의 제거가 가능하다.
- CurrentConditionDisplay class는 현재 기상 현황을 보여주는 기상 스테이션 APP의 기능항목 중 하나이다.
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 | public class CurrentConditionDisplay implements Observer, DisplayElement { private float temp; private float humidity; private float pressure; private Subject weatherData; // 느슨한 결합(Loose Coupling)을 위해 상위 형식(interface)으로 구성(has a)을 사용 // ==> 구체적인 구현에 맞춰 프로그래밍할 필요가 없어지고 확장시 문제발생 소지가 없다. // observer로 등록 또는 탈퇴하기 위한 용도로 가진다. 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; this.pressure = pressure; display(); } @Override public void display() { System.out.println("Current Condition: "+this.temp+"F degrees and "+this.humidity+"% humidity and "+pressure+" pressure"); } }
| cs |
- StatisticsDisplay class는 기상통계를 보여주는 기상 스테이션 APP의 기능항목 중 하나이다.
- ForecastDisplay class는 기상 예보를 보여주는 기상 스테이션 APP의 기능항목 중 하나이다.
4)모든 항목들은 display() 메소드를 공통으로 가져야 한다. 따라서 interface로 만들어 반드시 구현하도록 한다.
| public interface DisplayElement { public void display(); } | cs |
2.2.6 Test class
| public class WeatherStation { public static void main(String args[]) { WeatherData weatherData = new WeatherData(); CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay(weatherData); weatherData.setMeasurements(32, 65, 30); } } | cs |
2.2.7 디자인 원칙
서로 상호작용하는 객체 사이에서는 가능하면 느슨하게 결합(Losose Coupling)하는 디자인을 사용해야 한다.
상호 의존성을 최소화 함으로서 변경 사항이 생겨도 문제가 발생하지 않음
2.2.8 JDK에서 Observer Pattern을 이용한 부분
- Observable API (java.util.Observer interface, java.utl.Observable class)
구현보다는 구성에 맞춰서 프로그래밍한다는 디자인 원칙에 위배되기 때문에 제약이 많다.
이미 다른 super class를 확장하고 있는 class에는 Observable class의 기능을 추가할 수 없다.
- JavaBeans(MVC)
- Swing(GUI framework)
Button(주제) - listener(옵저버)