Programming/C++

C++ 옵저버 패턴

_SYPark 2021. 6. 13. 21:28
728x90

프로그램을 작성하다 보면 하나의 원본 데이터를 여러 객체가 동시에 참조하는 경우가 있습니다. 이럴 경우 원본 데이터의 값에 변화가 생겼을 때 데이터 값의 변화를 시켜줘야 합니다. 예를 들어 동일한 성적 데이터를 바라보고 있는 막대그래프, 꺾은선 그래프, 원형 그래프가 있을 때 데이터 쪽에서나 혹은 그래프 쪽에서 값을 변경한다면 모두에게 변화가 적용되어야 합니다. 이렇게 동일한 데이터를 여러 곳에서 사용하는 경우를 One Source Multiple Use라고 합니다.

 

단순히 직접 참조하도록 한다면 한 그래프에서 데이터를 변경할 경우 값이 변경은 되지만 다른 그래프에게 변경을 알려줄 수는 없고 원본 데이터의 데이터를 직접 참조하기 때문에 정보 은닉이 깨지게 됩니다.

 

첫 번째 문제를 해결하기 위해서는 중재자 패턴처럼 중간에 매개 객체를 사용하는 방법이 있습니다. 매개 객체는 원본 데이터를 저장 관리하는 객체가 적절합니다. 어느 객체에서 데이터를 변경했을 때 매개 객체에게 데이터가 변경됐음을 알려주고 모든 객체에게 전달해주게 됩니다. 두 번째 문제를 해결하기 위해 원본 데이터 객체 내부의 자료구조와는 무관하게 표현 객체들이 원본 데이터의 값을 참조할 수 있게 표준화된 자료구조를 제공하면 됩니다. 원본 데이터의 값을 완벽히 표현할 수 있는 자료구조를 별도로 정의해서 내부 자료구조와는 무관하게 별도로 정의된 자료구조를 통해 접근할 수 있도록 하게 하면 됩니다.

 

class ScoreCard {
private:
    string name;  int score;
};

class Subject {
    virtual void Attach(Observer* obj);
    virtual void Detach(Observer* obj);		// observer 리스트에서 추가 삭제
    virtual void Notify();			// observer 리스트에 등록된 모든 observer update 호출
protected:
    list<Observer*> observers;
};

class ScoreData : public Subject {
    void AddScore(ScoreCard* score);
    void RemoveScore(ScoreCard* score);
    list<ScoreCard*> GetScoreList();
    void SetScore(string name, int score) {
        list<ScoreCard*>::iterator iter;
        for( ;; ) { 
        	Notify();
            break;
        } // 이름으로 ScoreCard를 찾음
    }
private:    
    list<ScoreCard*> scores;
};

class Observer { virtual void Update() = 0; };
class BarGraph : public Observer {
    void Update {} // 정보 최신화
    void ChangeScore(string name, int score) { pScoreData->SetScore(name, score); }
private:
    ScoreData pScoreData;		// 공유하고 있는 정보
};

int main()
{
    ScoreData termScore;
    BarGarph bar(&termScore);
    LineGraph line(&termScore);
    
    ScoreCard stu1;
    stu1.name = "name";    stu1.score=100;
    termScore.AddScore(&stu1);
    
    //////
    
    bar.changeScore("name", 90);
}

실 점수를 관리하고 있는 ScoreData 객체와 표현을 위한 그래프 객체들 간에 Subject라는 객체를 두어 관리를 해주게 됩니다. 그리고 모든 객체에서 실 데이터는 ScoreCard라는 자료구조를 통해 모두 동일한 자료구조를 가지게 됩니다. 만약 다른 그래프에서 값을 변경한다면 데이터의 SetScore를 호출하고 SetScore에서는 Notify를 호출하게 됩니다. 마지막으로 Notify에서 등록된 모든 그래프의 update를 호출하면서 하나의 그래프에서 값을 변경하더라도 그 데이터를 참조하고 있는 모든 객체가 새로운 값으로 갱신하게 됩니다.

 

옵저버 패턴에서 고려해볼 사항은 현재 매개 객체에서 Notify시 등록된 모든 옵저버에게 update를 호출하는데 만약 옵저버 개수가 많다면 모든 옵저버가 호출하는 것은 비효율적일 수 있으므로 관련된 객체만 update 할 수 있게 인자를 두는 방법도 있습니다.

 

728x90