옵저버 패턴: 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의 변경을 통보 받는 클래스