디자인 패턴 – 옵저버 패턴

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

Leave a Reply

Your email address will not be published. Required fields are marked *