옵저버 패턴: DataProvider와 DataConsumer가 나눠져 있는 경우 사용하는 패턴
기존 코드: ScoreRecord는 성적을 업데이트하는 객체로 DataProvider에 해당됨, DataSheetView는 성적을 통보받는 객체로 DataConsumer에 해당됨
import java.util.ArrayList; class DataSheetView{ private ScoreRecord scoreRecord; private int viewCount; public DataSheetView(ScoreRecord scoreRecord, int viewCount) { this.scoreRecord=scoreRecord; this.viewCount=viewCount; } public void update() { ArrayList<Integer> record=scoreRecord.getScoreRecord(); // 점수를 조회함 displayScores(record,viewCount); // 조회된 점수를 viewCount만큼 출력함 } private void displayScores(ArrayList<Integer> record, int viewCount) { System.out.print("List of "+viewCount+" entries: "); for(int i=0; i<viewCount && i<record.size(); i++) System.out.print(record.get(i)+" "); System.out.println(); } } class ScoreRecord { private ArrayList<Integer> scores=new ArrayList<Integer>(); private DataSheetView dataSheetView; public void setDataSheetView(DataSheetView dataSheetView) { this.dataSheetView=dataSheetView; } public void addScore(int score) { // 새로운 점수를 추가함 scores.add(score); // scores 목록에 주어진 점수를 추가함 dataSheetView.update(); // scores가 변경됨을 통보함 } public ArrayList<Integer> getScoreRecord(){ return scores; } } public class Client{ public static void main(String[] args) { ScoreRecord scoreRecord=new ScoreRecord(); // 3개까지의 점수만 출력함 DataSheetView dataSheetView=new DataSheetView(scoreRecord,3); scoreRecord.setDataSheetView(dataSheetView); for(int index=1; index<=5; index++) { int score=index*10; System.out.println("Adding "+score); // 10 20 30 40 50을 추가함, 추가할 때마다 최대 3개의 점수만 출력함 scoreRecord.addScore(score); } } }
문제점: 성적의 최고점과 최저점만 통보받는 Consumer(MinMaxView)가 생긴다면, Provider(ScoreRecord)의 코드도 변경해야 된다. DataProvider는 DataConsumer를 직접적으로 모르는 것이 더 좋다. (단, Consumer는 Provider를 알아도 무방함)
해결 방법: Subject 클래스를 만들어서 Subject에서 Notification을 진행한다. Notification을 받는 객체는 Observer라는 인터페이스로 제작한다. DataProvider는 Subject를 상속받아 통보받는 객체를 간접적으로 파악할 수 있다. DataConsumer는 Observer를 구현해서 업데이트를 알림받을 수 있다.
import java.util.ArrayList; import java.util.Collections; import java.util.List; interface Observer{ // 추상화된 통보 대상 abstract public void update(); } abstract class Subject{ private List<Observer> observers=new ArrayList<Observer>(); public void attach(Observer observer) { // 옵저버(통보 대상)를 추가함 observers.add(observer); } public void detach(Observer observer) { // 옵저버(통보 대상)을 제거함 observers.remove(observer); } // 각 옵저버에게 변경을 통보함 public void notifyObservers() { for(Observer o: observers) o.update(); } } class DataSheetView implements Observer{ private ScoreRecord scoreRecord; private int viewCount; public DataSheetView(ScoreRecord scoreRecord, int viewCount) { this.scoreRecord=scoreRecord; this.viewCount=viewCount; } public void update() { ArrayList<Integer> record=scoreRecord.getScoreRecord(); // 점수를 조회함 displayScores(record,viewCount); // 조회된 점수를 viewCount만큼 출력함 } private void displayScores(ArrayList<Integer> record, int viewCount) { System.out.print("List of "+viewCount+" entries: "); for(int i=0; i<viewCount && i<record.size(); i++) System.out.print(record.get(i)+" "); System.out.println(); } } class MinMaxView implements Observer{ private ScoreRecord scoreRecord; public MinMaxView(ScoreRecord scoreRecord) { this.scoreRecord=scoreRecord; } public void update() { List<Integer> record=scoreRecord.getScoreRecord(); displayMinMax(record); // 최소,최댓값만을 출력 } private void displayMinMax(List<Integer> record) { int min=Collections.min(record,null); int max=Collections.max(record,null); System.out.println("Min: "+min+" Max: "+max); } } class ScoreRecord extends Subject{ private ArrayList<Integer> scores=new ArrayList<Integer>(); public void addScore(int score) { scores.add(score); // 각 옵저버에게 데이터의 변경을 통보함 notifyObservers(); } public ArrayList<Integer> getScoreRecord(){ return scores; } } public class Client{ public static void main(String[] args) { ScoreRecord scoreRecord=new ScoreRecord(); // 3개까지의 점수만 출력하는 DataConsumer를 추가함 DataSheetView dataSheetView3=new DataSheetView(scoreRecord,3); scoreRecord.attach(dataSheetView3); // MinMax 점수를 출력하는 DataConsumer를 추가함 MinMaxView minMaxView=new MinMaxView(scoreRecord); scoreRecord.attach(minMaxView); for(int index=1; index<=5; index++) { int score=index*10; System.out.println("Adding "+score); // 10 20 30 40 50을 추가함, 추가할 때마다 최대 3개의 점수만 출력함 scoreRecord.addScore(score); } } }
옵저버패턴의 컬레보레이션
Subject: ConcreteObserver 객체를 관리하는 클래스
ConcreteSubject: 변경 관리 대상이 되는 데이터가 있는 클래스
Observer: 데이터의 변경을 통보받는 인터페이스
ConcreteObserver: ConcreteSubject의 변경을 통보 받는 클래스