ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Design Pattern] Observer Pattern
    Programming/DesignPattern 2020. 12. 26. 17:19

    Observer Pattern

    옵저버 패턴은 데이터의 변경이 발생되었을 때, 상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 유용하다.

    예를 들어, 새로운 파일이 추가되거나 기존 파일이 삭제되었을 때 탐색기는 이를 즉시 표시할 수 있다. 탐색기를 복수 개 실행하는 상황이나 하나의 탐색기에서 파일 시스템을 변경했을 때는 다른 탐색기에게 즉각적으로 이 변경을 통보해야 한다.

    다른 예로는 차량의 연료가 소진될 때까지 주행가능 거리를 출력하는 클래스, 연료량이 부족하면 경고 메세지를 보내는 클래스, 연료량이 부족하면 자동으로 근처 주유소를 표시하는 클래스 등에 연료량의 변화를 통보하는 경우가 있다.

    이런 경우 연료량 클래스는 연료량에 관심을 가지는 클래스에 직접 의존하지 않는 방식으로 설계하는 것이 바람직하다.

    옵저버 패턴은 통보 대상 객체의 관리를 Subject 클래스와 Observer 인터페이스로 일반화한다. 그러면 데이터 변경을 통보하는 클래스(ConcreteSubject)는 통보 대상 클래스나 객체에 대한 의존성을 없앨 수 있다. 결과적으로 옵저버 패턴은 통보 대상 클래스나 대상 객체의 변경에도 ConcreteSubject 클래스를 수정없이 그대로 사용할 수 있도록 한다.


    옵저버 패턴의 모델

    • Observer : 데이터의 변경을 통보 받는 인터페이스. 즉, Subject에서는 Observer 인터페이스의 update() 를 호출함으로써 ConcreteSubject의 데이터 변경을 Concrete에게 통보한다.
    • Subject: ConcreteSubject 객체를 관리하는 요소. Observer 인터페이스를 참조해서 ConcreteObserver를 관리하므로 ConctreteObserver 변화에 독립적일 수 있다.
    • ConcreteSubject : 변경 관리 대상이 되는 데이터가 있는 클래스. 데이터 변경을 위한 메서드인 setState가 있으며, setState에서는 자신의 데이터인 subjectState를 변경하고 Subject의 notifyObservers를 호출해서 Concrete Observer 객체에 변경을 통보한다
      • ScoredRecored
    • ConcreteObserver : ConcreteSubject의 변경을 통보받는 클래스. Observer 인터페이스의 update 메서드를 구현함으로써 변경을 통보받는다. 변경된데이터는 ConcreteSubject의 getState메서드를 호출함으로써 변경을 조회한다.
      • DataSheetView, MinMaxView

     

    /**
     * 입력하는 점수를 저장하는 클래스
     * 구체적인 변경 감시 대상 데이터
     */
    public class ScoredRecord extends Subject{
        private List<Integer> scores = new ArrayList<Integer>();
    
        //데이터가 변경될 때 View 에게 통지한다.
        public void addScore(int score) { //새로운 점수를 추가
            scores.add(score); //점수 리스트에 점수 추가
            //데이터가 변경되면 Subject 클래스의 notifyObservers 메서드를 호출해
            // 각 옵저버에게 데이터 변경을 통보
            notifyObservers();
        }
    
        public List<Integer> getScoreRecord(){
            return scores;
        }
    }
    
    
    /**
     * 성적 변경에 관심이 있는 대상 객체를 관리하는 Subject 클래스를 추가 정의
     * Subject 는 Observer 인터페이스를 구현함으로써 성적 변경에 관심이 있음을 보여준다.
     * Subject 클래스는 Observer 인터페이스를 통해 View 객체들의 update 메서드를 호출한다.
     */
    
    //추상화된 변경 관심 대상 데이터
    public abstract class Subject {
        private List<Observer> observers = new ArrayList<>(); //추상화된 통보 대상 목록
    
        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();
        }
    }
    
    /**
     * 추상화된 통보 대상
     */
    public interface Observer {
        public abstract void update(); //데이터 변경을 통보했을 때 처리하는 메서드
    }
    
    public class MinMaxView implements Observer{
        private ScoredRecord scoredRecord;
    
        public MinMaxView(ScoredRecord scoredRecord){
            this.scoredRecord = scoredRecord;
        }
    
        public void update() { //점수의 변경을 통보받음
            List<Integer> record = scoredRecord.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 DataSheetView implements Observer {
        private ScoredRecord scoredRecord;
        private int viewCount;
    
        public DataSheetView(ScoredRecord scoredRecord, int viewCount){
            this.scoredRecord = scoredRecord;
            this.viewCount = viewCount;
        }
    
        public void update() { //점수의 변경을 통보받음
            List<Integer> record = scoredRecord.getScoreRecord(); //변경된 scores 리스트를 얻어서 표시
            displayScores(record);
        }
    
        private void displayScores(List<Integer> record) {
            for (int i=0; i<viewCount && i<record.size(); i++){
                System.out.println(record.get(i) + " ");
            }
            System.out.println();
        }
    }
    
    
    /**
     * [문제점]
     * 데이터의 통보대상이 변경된 것을 반영하기 위해 ScoredRecord 클래스의 코드를 수정해야 한다.
     * 예를 들어, 평균/표준 편차를 출력하는 클래스에게 성적변경을 통보하려면 ScoredRecord 를 다시 변경해야한다.
     *
     * [해결책]
     * 성적 통보 대상이 변경(추가/삭제)되더라도 ScoreRecord 클래스를 그대로 재사용할 수 있어야 한다.
     * 따라서 Scored 클래스에서 변화되는 부분을 식별하고, 이를 일반화 시켜야한다.
     */
    public class Client {
        public static void main(String[] args) {
            ScoredRecord scoredRecord = new ScoredRecord();
    
            DataSheetView dataSheetView3 = new DataSheetView(scoredRecord, 3);
            DataSheetView dataSheetView5 = new DataSheetView(scoredRecord, 5);
            MinMaxView minMaxView = new MinMaxView(scoredRecord);
            StatisticsView statisticsView = new StatisticsView(scoredRecord);
    
            scoredRecord.attach(dataSheetView3);
            scoredRecord.attach(dataSheetView5);
            scoredRecord.attach(minMaxView);
    
            scoredRecord.detach(dataSheetView3);
            scoredRecord.detach(dataSheetView5);
            scoredRecord.attach(statisticsView);
    
            for (int i=0; i<=5; i++){
                //데이터가 추가될 때 마다 , dataSheetView3 / dataSheetView5 / minMaxView 의 값이 출력됨
                scoredRecord.addScore(i*10);
            }
    
    
    
        }
    }
    
    

    'Programming > DesignPattern' 카테고리의 다른 글

    [Design Pattern] Command Pattern  (0) 2020.12.17
    [Design Pattern] State Pattern  (0) 2020.12.15
    [Design Pattern] Singleton Pattern  (1) 2020.12.14
    [Design Pattern] Strategy Pattern  (0) 2020.12.10
Designed by Tistory.