옵저버 패턴
정의
- 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해
객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다. - 다른 객체의 상태 변화를 별도의 함수 호출 없이 즉각적으로 알 수 있기 때문에
이벤트에 대한 처리를 자주해야 하는 프로그램에 매우 효율적이다.
장점
- 실시간으로 한 객체의 변경사항을 다른 객체에 전파할 수 있다.
- 느슨한 결합으로 시스템이 유여하고 객체간의 의존성을 제거할 수 있다.
단점
- 너무 많이 사용할 경우 상태 관리가 힘들 수 있다.
- 데이터 배분에 문제가 발생하면 큰 문제로 발전할 수 있다.
옵저버 패턴 적용전
Client
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);
}
}
}
분석
- 처음에 ScoreRecord 라는 점수저장 클래스 선언
- DataSheetView에 파라미터로 ScoreRecord와 몇개의 값을 뽑아낼지 정하는 정수까지해서 총 2개를 준다
ScoreRecord
import java.util.ArrayList;
public 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;
}
}
DataSheetView
import java.util.ArrayList;
public 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();
}
}
문제점
- 성적을 다른 방식으로 출력하고 싶다면 어떤 변경 작업을 해야하는가?
- 성적을 동시에 여러 가지 형태로 출력하려면 어떤 변경 작업을 해야하는가?
- ex. 성적이 입력되면 최대 3개 목록 출력, 최대 5개 목록출력, 최소/최대값만을 출력
- 프로그램 실행 시에 성적의 출력 대상이 변경되는 것을 지원한다면 어떤 변경 작업을 해야하는가?
해결법
- 결국 가장 많이 변경되는건 ScoreRecord에 addScore메서드이다. 따라서 그부분만 고치면 된다 → 옵서버패턴 적용
패턴 적용 후
패턴 적용 그림
패턴 적용 코드
Client
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);
for (int index = 1; index <= 5; index++) {
int score = index * 10;
System.out.println("Adding " + score);
scoreRecord.addScore(score);
}
}
}
Subject
import java.util.ArrayList;
import java.util.List;
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);
}
// 통보 대상 목록, 즉 observers의 각 옵서버에게 변경을 통보함
public void notifyObservers() {
for (Observer o : observers)
o.update();
}
}
ScoreRecord
import java.util.ArrayList;
import java.util.List;
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;
}
}
Observer
public interface Observer {
abstract public void update() ;
}
DataSheetView
import java.util.List;
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> record = 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();
}
}
MinMaxView
import java.util.Collections;
import java.util.List;
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);
}
}
출처
- JAVA 객체 지향 디자인 패턴(저자: 정인상, 채홍석)
- https://velog.io/@octo__/%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4Observer-Pattern
'디자인 패턴' 카테고리의 다른 글
[디자인 패턴] 템플릿 메서드 패턴 (1) | 2023.12.01 |
---|---|
[디자인패턴] 데커레이터 패턴 (0) | 2023.12.01 |
[디자인패턴] 커맨드 패턴 (0) | 2023.10.29 |
[디자인패턴]빌더 패턴 (0) | 2023.10.12 |
[디자인패턴]싱글톤(Singleton)패턴 (0) | 2023.10.03 |