4. 옵서버 패턴(Observer Pattern)
옵서버 패턴이란?
옵서버 패턴은 데이터의 변경이 발생했을 경우 상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 유용하다. 통보 대상 클래스나 대상 객체의 변경에도 데이터 변경을 통보하는 클래스를 수정 없이 그대로 사용할 수 있도록 한다.
다음 예제를 보며 이해해보자
성적을 출력하는 예시 코드가 있다.
//점수 클래스
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); //scores 목록에 주어진 점수를 추가함
dataSheetView.update(); //scores가 변경됨을 통보함
}
public List<Integer> getScoreRecore(){
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> 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();
}
}
//클라이언트 클래스
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);
}
}
}
위의 코드에서 성적을 다른 형태로 출력하고 싶다면 어떻게 해야할까?
현재 상황에서는 dataSheetView 클래스의 변경이 필수가 된다.
더 나아가 현재3개씩 출력하는 방식 말고도 최대/최소값을 출력하는 방법을 함께 추가하고 싶다면?
ScoreRecord에 2개의 dataSheetView 를 사용해야하므로 ScoreRecord의 변경이 필수가 된다.
아래처럼 옵서버 패턴으로 수정하면 ScoreRecord 클래스의 변경없이도 다양한 출력방법을 적용할 수 있다.
옵서버 패턴을 적용한 수정된 에제코드
//옵서버 인터페이스
public interface Observer { //추상화된 통보 대상
public abstract void update(); //데이터의 변경을 통보했을 때 처리하는 메소드
}
//주제 클래스
public abstract class Subject { //추상화된 변경 관심 대상 데이터
private List<Observer> observers = new ArrayList<Observet>(); //추상화된 통보 대상 목록
public void attach(Observer observer){ //옵서버, 즉 통보 대상을 추가함
observers.add(observer);
}
public void detach(Observer observer){ //옵서버, 즉 통보 대상을 제거함
observer.remove(observer);
}
//통보 대상 목록, 즉 observers의 각 옵서버에게 변경을 통보함
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> 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();
}
}
//최대, 최소값 출력 클래스
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();
MinmaxView minMaxView = new MinMaxView(scoreRecord);
scoreRecord.setMinMaxView(minMaxView);
for(int index = 1; inex <= 5; index++){
int score = index * 10;
System.out.println("Adding " + score);
//10 20 30 40 50이 추가됨, 추가할 때마다 최소/최대 값을 출력함
scoreRecord.addScore(score);
}
}
}
해당 포스트는 JAVA 객체지향 디자인패턴을 읽은 후 기록한 내용입니다.
참고서적링크 : https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=38792551
자바 객체지향 디자인 패턴
체계적인 학습법을 바탕으로 설명하는 객체지향 디자인 패턴의 교과서. 자바와 UML을 중심으로 객체지향 이론이 무엇인지를 배운 다음 GoF에서 소개하는 디자인 패턴의 핵심 10가지를 알기 쉽게
www.aladin.co.kr
이미지 아이콘 출처: https://www.flaticon.com/free-icon/pattern-lock_4643427?term=pattern&related_id=4643427