Design pattern
[Design Pattern] Observer Pattern
s00ng
2022. 8. 4. 22:15
1. 옵저버 패턴이란?
데이터의 변경이 발생했을 경우,그와 연관된 객체들에게 알림을 보내는 디자인 패턴
- 상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 유용
- 통보 대상 객체 관리를 Subject 클래스와 Observer 인터페이스로 일반화
- 통보 대상 클래스나 대상 객체의 변경에도 ConcreteSubject 클래스를 수정 없이 그대로 사용할 수 있도록 함
Observer: 데이터의 변경을 통보 받는 인터페이스. Subject에서는 Observer 인터페이스의 update 메서드를 호출함으로써 ConcreteSubject의 데이터 변경을 ConcreteObserver에게 통보
Subject: ConcreteObserver 객체를 관리하는 요소. Observer 인터페이스를 참조해서 Concrete Observer를 관리하므로 ConcreteObserver의 변화에 독립적
ConcreteSubject: 변경 관리 대상이 되는 데이터가 있는 클래스. 데이터의 변경을 위한 메서드인 setState가 있으며 setState 에서는 자신의 데이터인 subjectState를 변경하고 Subject의 notifyObservers 메서드를 호출해서 ConcreteObserver 객체에 변경을 통보
ConcreteObserver: ConcreteSubject의 변경을 통보받는 클래스. Observer 인터페이스의 update 메서드를 구현함으로써 변경을 통보 받음. 변경된 데이터는 ConcreteSubject의 getState 메서드를 호출함으로써 변경을 조회
2. 옵서버 패턴 예시
/* 옵저버 패턴 사용하지 않은 코드*/
public class ScoreRecord {
private List<Integer> scores = new ArrayList<Integer>(); // 점수 저장
private DataSheetView dataSheetView; // 목록 형태로 점수를 출력하는 클래스
public void setDataSheetView(DataSheetView dataSheetView){
this.dataSheetView = dataSheetView;
}
public void addScore(int score){ // 새로운 점수 추가
scores.add(score);
dataSheetView.update();
}
public List<Integer> getScoreRecord(){
return scores;
}
}
public class DataSheetView {
private ScoreRecord scoreRecord;
private int viewCount;
public DataSheetView(ScoreRecord scoreRecord, int viewCount){
this.scoreRecord = scoreRecord;
this.viewCount = viewCount;
}
public void update(){ //점수 변경을 통보 받음
List<Integer> recore = scoreRecord.getScoreRecord(); // 점수 조회
displayScores(record, viewCount); // 조회된 점수를 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 Client {
public static void main(String[] args){
ScoreRecord scoreRecord = new ScoreRecord();
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);
scoreRecord.addScore(score);
}
}
}
문제점
- 성적을 다른 형태로 출력하는 경우 → 현재 점수가 입력되었을 때 지정된 특정 대상 클래스에게 고정적으로 통보하도록 코딩이 되어있으므로, 다른 대상 클래스에게 점수가 입력되었음을 통보하려면 ScoreRecord 클래스의 변경이 필요 (OCP 위반)
- 동시 혹은 순차적으로 성적을 출력하는 경우 → 이 역시 성적의 통보 대상이 변경된 것을 반영하기 위해 ScoreRecord 클래스의 코드를 수정해야함(OCP 위반)
/* 옵저버 패턴 사용 코드 */
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);
// 데이터가 변경되면 Subject 클래스의 notifyObservers 메서드를 호출해
// 각 옵저버에게 데이터 변경 통보
notifyObservers();
}
public List<Integer> getScoreRecord(){
return scores;
}
}
//DataSheetView는 Observer의 기능, update 메서드를 구현함으로써 통보 대상이 됨
public 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(){ //점수 변경을 통보 받음
List<Integer> recore = scoreRecord.getScoreRecord(); // 점수 조회
displayScores(record, viewCount); // 조회된 점수를 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;
}
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);
}
}
public class Client {
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);
//3개 목록 DataSheetView를 ScoreRecord에 Observer로 추가
scoreRecord.attach(dataSheetView3);
//5개 목록 DataSheetView를 ScoreRecord에 Observer로 추가
scoreRecord.attach(dataSheetView5);
//MinMaxView를 Scorerecord에 Observer로 추가
scoreRecord.attach(minMaxView);
}
}