디자인 패턴

[디자인패턴] 옵저버 패턴

Ash_jisu 2023. 10. 31. 13:37

옵저버 패턴

정의

  • 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해
    객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다.
  • 다른 객체의 상태 변화를 별도의 함수 호출 없이 즉각적으로 알 수 있기 때문에 
    이벤트에 대한 처리를 자주해야 하는 프로그램에 매우 효율적이다.

 

장점

  • 실시간으로 한 객체의 변경사항을 다른 객체에 전파할 수 있다.
  • 느슨한 결합으로 시스템이 유여하고 객체간의 의존성을 제거할 수 있다.

단점

  • 너무 많이 사용할 경우 상태 관리가 힘들 수 있다.
  • 데이터 배분에 문제가 발생하면 큰 문제로 발전할 수 있다.

 

 

 

 

 


옵저버 패턴 적용전

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();
	}
}

문제점

  1. 성적을 다른 방식으로 출력하고 싶다면 어떤 변경 작업을 해야하는가?
  2. 성적을 동시에 여러 가지 형태로 출력하려면 어떤 변경 작업을 해야하는가?
    • ex. 성적이 입력되면 최대 3개 목록 출력, 최대 5개 목록출력, 최소/최대값만을 출력
  3. 프로그램 실행 시에 성적의 출력 대상이 변경되는 것을 지원한다면 어떤 변경 작업을 해야하는가?

 

해결법

  • 결국 가장 많이 변경되는건 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

- https://velog.io/@haero_kim/%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4-%EA%B0%9C%EB%85%90-%EB%96%A0%EB%A8%B9%EC%97%AC%EB%93%9C%EB%A6%BD%EB%8B%88%EB%8B%A4