[Design Pattern] 옵서버 패턴(Observer Pattern)
Posted: Updated:
자바 디자인 패턴 스터디를 하며 ‘Java 객체 지향 디자인 패턴’ 교재를 정리한 글입니다.
옵서버 패턴(Observer Pattern)
- 객체의 상태 변화를 관찰하는 관찰자(옵저버)들의 목록을 객체에 등록
- 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지
- 통보 대상 객체 관리를
Subject
클래스와Observer
인터페이스로 일반화 - 데이터 변경을 통보하는 클래스
ConcreteSubject
는 통보 대상 클래스/객체ConcreteObserver
에 대한 의존성 제거 ConcreteSubject
클래스를 수정 없이 사용 가능
옵서버 패턴 컬레보레이션
- Observer
- 데이터의 변경을 통보 받는 인터페이스
Subject
에서update()
호출 시ConcreteSubject
의 데이터 변경을ConcreteObserver
에게 통보
- Subject
ConcreteObserver
객체를 관리하는 요소Observer
인터페이스를 참조해서 관리ConcreteObserver
의 변화에서 독립적
- ConcreteSubject
- 변경 관리 대상 데이터가 있는 클래스
setState
: 데이터 변경을 위한 메서드- 자신의 데이터인
subjectState
를 변경 Subject
의notifyObservers
메서드를 호출하고ConcreteObserver
객체에 변경 통보
- 자신의 데이터인
- ConcreteObserver
ConcreteSubject
의 변경을 통보 받는 클래스update
메서드를 구현해서 변경을 통보 받음- 변경된 데이터는
ConcreteSubject
의getState
메서들을 호출해 변경 조회
예시: 성적 출력하기
- 리스트에 성적을 추가하면 원하는 방식으로 성적 리스트를 출력할 수 있음
- n개 목록, 최소/최대 값, 합/평균 총 3가지 출력 방식
Observer
: Observer,update()
를 제공하는 인터페이스Subject
: Subject,ScoreRecord
객체 관리ScoreRecord
: ConcreteSubject, 성적 데이터를 가진 클래스, 성적을 추가하면notifyObservers()
를 통해 데이터 변경 통보DataSheetView
,MinMaxView
,StatisticsView
: ConcreteObserver, 외부에서ConcreteSubject
의notifyObservers()
를 통해update
메서드가 실행되면 해당 방식으로 성적 리스트 출력
전체 코드
public interface Observer {
public abstract void update();
}
public 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();
}
}
}
public class ScoreRecord extends Subject{
private List<Integer> scores = new ArrayList<Integer>();
public void addScore(int score) {
scores.add(score);
notifyObservers();
}
public List<Integer> getScoreRecord() {
return scores;
}
}
public class DataSheetView implements Observer{
private ScoreRecord scoreRecord;
private int viewCount;
public DataSheetView(ScoreRecord scoreRecord, int viewCount) {
this.scoreRecord = scoreRecord;
this.viewCount = viewCount;
}
@Override
public void update() {
List<Integer> record = scoreRecord.getScoreRecord();
displayScores(record, viewCount);
}
private void displayScores(List<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();
}
}
public class MinMaxView implements Observer{
private ScoreRecord scoreRecord;
public MinMaxView(ScoreRecord scoreRecord) {
this.scoreRecord = scoreRecord;
}
@Override
public void update() {
List<Integer> record = scoreRecord.getScoreRecord();
displayMinMax(record);
}
public void displayMinMax(List<Integer> record) {
int min = Collections.min(record, null);
int max = Collections.max(record, null);
System.out.println("Min: "+min+" Max: "+max);
}
}
public class StatisticsView implements Observer{
private ScoreRecord scoreRecord;
public StatisticsView(ScoreRecord scoreRecord) {
this.scoreRecord = scoreRecord;
}
@Override
public void update() {
List<Integer> record = scoreRecord.getScoreRecord();
displayStatistics(record);
}
private void displayStatistics(List<Integer> record) {
int sum = 0;
for (int score : record) {
sum += score;
}
float average = (float) sum / record.size();
System.out.println("Sum: "+sum+" Average: "+average);
}
}
public class Main {
public static void main(String[] args) {
ScoreRecord scoreRecord = new ScoreRecord();
DataSheetView dataSheetView3 = new DataSheetView(scoreRecord, 3);
DataSheetView dataSheetView5 = new DataSheetView(scoreRecord, 5);
MinMaxView minMaxView = new MinMaxView(scoreRecord);
scoreRecord.attach(dataSheetView3);
scoreRecord.attach(dataSheetView5);
scoreRecord.attach(minMaxView);
System.out.println("---3개 목록, 5개 목록, MinMax 관찰자 추가---");
for (int index = 1; index <= 5; index++) {
int score = index * 10;
System.out.println("Adding "+score);
scoreRecord.addScore(score);
}
scoreRecord.detach(dataSheetView3);
System.out.println();
System.out.println("---3개 목록 관찰자 삭제 후 합/평균 관찰자 추가---");
StatisticsView statisticsView = new StatisticsView(scoreRecord);
scoreRecord.attach(statisticsView);
for (int index = 1; index <= 5; index++) {
int score = index * 10;
System.out.println("Adding "+score);
scoreRecord.addScore(score);
}
}
}
출력
---3개 목록, 5개 목록, MinMax 관찰자 추가---
Adding 10
List of 3 entries: 10
List of 5 entries: 10
Min: 10 Max: 10
Adding 20
List of 3 entries: 10 20
List of 5 entries: 10 20
Min: 10 Max: 20
Adding 30
List of 3 entries: 10 20 30
List of 5 entries: 10 20 30
Min: 10 Max: 30
Adding 40
List of 3 entries: 10 20 30
List of 5 entries: 10 20 30 40
Min: 10 Max: 40
Adding 50
List of 3 entries: 10 20 30
List of 5 entries: 10 20 30 40 50
Min: 10 Max: 50
---3개 목록 관찰자 삭제 후 합/평균 관찰자 추가---
Adding 10
List of 5 entries: 10 20 30 40 50
Min: 10 Max: 50
Sum: 160 Average: 26.666666
Adding 20
List of 5 entries: 10 20 30 40 50
Min: 10 Max: 50
Sum: 180 Average: 25.714285
Adding 30
List of 5 entries: 10 20 30 40 50
Min: 10 Max: 50
Sum: 210 Average: 26.25
Adding 40
List of 5 entries: 10 20 30 40 50
Min: 10 Max: 50
Sum: 250 Average: 27.777779
Adding 50
List of 5 entries: 10 20 30 40 50
Min: 10 Max: 50
Sum: 300 Average: 30.0
댓글남기기