Posted:      Updated:

자바 디자인 패턴 스터디를 하며 ‘Java 객체 지향 디자인 패턴’ 교재를 정리한 글입니다.

옵서버 패턴(Observer Pattern)

  • 객체의 상태 변화를 관찰하는 관찰자(옵저버)들의 목록을 객체에 등록
  • 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지
  • 통보 대상 객체 관리를 Subject 클래스와 Observer 인터페이스로 일반화
  • 데이터 변경을 통보하는 클래스 ConcreteSubject 는 통보 대상 클래스/객체 ConcreteObserver 에 대한 의존성 제거
  • ConcreteSubject 클래스를 수정 없이 사용 가능

옵서버 패턴 컬레보레이션

  • Observer
    • 데이터의 변경을 통보 받는 인터페이스
    • Subject 에서 update() 호출 시 ConcreteSubject 의 데이터 변경을 ConcreteObserver 에게 통보
  • Subject
    • ConcreteObserver 객체를 관리하는 요소
    • Observer 인터페이스를 참조해서 관리
    • ConcreteObserver 의 변화에서 독립적
  • ConcreteSubject
    • 변경 관리 대상 데이터가 있는 클래스
    • setState : 데이터 변경을 위한 메서드
      • 자신의 데이터인 subjectState 를 변경
      • SubjectnotifyObservers 메서드를 호출하고 ConcreteObserver 객체에 변경 통보
  • ConcreteObserver
    • ConcreteSubject 의 변경을 통보 받는 클래스
    • update 메서드를 구현해서 변경을 통보 받음
    • 변경된 데이터는 ConcreteSubjectgetState 메서들을 호출해 변경 조회

예시: 성적 출력하기

  • 리스트에 성적을 추가하면 원하는 방식으로 성적 리스트를 출력할 수 있음
  • n개 목록, 최소/최대 값, 합/평균 총 3가지 출력 방식
  • Observer : Observer, update() 를 제공하는 인터페이스
  • Subject : Subject, ScoreRecord 객체 관리
  • ScoreRecord : ConcreteSubject, 성적 데이터를 가진 클래스, 성적을 추가하면 notifyObservers() 를 통해 데이터 변경 통보
  • DataSheetView , MinMaxView , StatisticsView : ConcreteObserver, 외부에서 ConcreteSubjectnotifyObservers() 를 통해 update 메서드가 실행되면 해당 방식으로 성적 리스트 출력

전체 코드

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

    public List<Integer> getScoreRecord() {
        return scores;
    }
}

public class DataSheetView implements Observer{
    private ScoreRecord scoreRecord;
    private int viewCount;

    public DataSheetView(ScoreRecord scoreRecord, int viewCount) {
        this.scoreRecord = scoreRecord;
        this.viewCount = viewCount;
    }

    @Override
    public void update() {
        List<Integer> record = scoreRecord.getScoreRecord();
        displayScores(record, 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;
    }

    @Override
    public void update() {
        List<Integer> record = scoreRecord.getScoreRecord();
        displayMinMax(record);
    }

    public 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 StatisticsView implements Observer{
    private ScoreRecord scoreRecord;

    public StatisticsView(ScoreRecord scoreRecord) {
        this.scoreRecord = scoreRecord;
    }

    @Override
    public void update() {
        List<Integer> record = scoreRecord.getScoreRecord();
        displayStatistics(record);
    }

    private void displayStatistics(List<Integer> record) {
        int sum = 0;
        for (int score : record) {
            sum += score;
        }
        float average = (float) sum / record.size();
        System.out.println("Sum: "+sum+" Average: "+average);
    }
}

public class Main {
    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);

        scoreRecord.attach(dataSheetView3);
        scoreRecord.attach(dataSheetView5);
        scoreRecord.attach(minMaxView);

        System.out.println("---3개 목록, 5개 목록, MinMax 관찰자 추가---");

        for (int index = 1; index <= 5; index++) {
            int score = index * 10;
            System.out.println("Adding "+score);
            scoreRecord.addScore(score);
        }

        scoreRecord.detach(dataSheetView3);

        System.out.println();
        System.out.println("---3개 목록 관찰자 삭제 후 합/평균 관찰자 추가---");

        StatisticsView statisticsView = new StatisticsView(scoreRecord);
        scoreRecord.attach(statisticsView);

        for (int index = 1; index <= 5; index++) {
            int score = index * 10;
            System.out.println("Adding "+score);
            scoreRecord.addScore(score);
        }
    }
}

출력

---3개 목록, 5개 목록, MinMax 관찰자 추가---
Adding 10
List of 3 entries: 10 
List of 5 entries: 10 
Min: 10 Max: 10
Adding 20
List of 3 entries: 10 20 
List of 5 entries: 10 20 
Min: 10 Max: 20
Adding 30
List of 3 entries: 10 20 30 
List of 5 entries: 10 20 30 
Min: 10 Max: 30
Adding 40
List of 3 entries: 10 20 30 
List of 5 entries: 10 20 30 40 
Min: 10 Max: 40
Adding 50
List of 3 entries: 10 20 30 
List of 5 entries: 10 20 30 40 50 
Min: 10 Max: 50

---3개 목록 관찰자 삭제 후 합/평균 관찰자 추가---
Adding 10
List of 5 entries: 10 20 30 40 50 
Min: 10 Max: 50
Sum: 160 Average: 26.666666
Adding 20
List of 5 entries: 10 20 30 40 50 
Min: 10 Max: 50
Sum: 180 Average: 25.714285
Adding 30
List of 5 entries: 10 20 30 40 50 
Min: 10 Max: 50
Sum: 210 Average: 26.25
Adding 40
List of 5 entries: 10 20 30 40 50 
Min: 10 Max: 50
Sum: 250 Average: 27.777779
Adding 50
List of 5 entries: 10 20 30 40 50 
Min: 10 Max: 50
Sum: 300 Average: 30.0

댓글남기기