프로그래밍 검색 블로그

옵저버 패턴 Observer Pattern [Class 구조] 본문

C++ 패턴

옵저버 패턴 Observer Pattern [Class 구조]

코딩조무사 2017. 10. 3. 20:14

API 디자인은 자바쪽을 보고 했다. 

다만 observer 호출 쪽을 비동기로 진행 



1. 전달받는 타입 


1
2
3
4
5
6
7
8
9
10
11
template<typename _ArgumentType>
class Observable;
 
template<typename _ArgumentType>
struct Observer{
    using dispatcher_type = const Observable<_ArgumentType>*;
    virtual void OnUpdate(dispatcher_type object, _ArgumentType argument) = 0;
    virtual ~Observer() {}
};
 
 
cs



2. 전달하는 타입 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <vector>
#include <algorithm>
#include <functional>
#include <list>
#include <type_traits>
#include <future>
 
template<typename _ArgumentType>
class Observable{
    using _MyObserverPointer = Observer<_ArgumentType>*;
    std::list<_MyObserverPointer> mObservers;
    
    using synchronized = std::lock_guard<std::mutex>;
    std::mutex mObserverMutex;
    
 
public:
    void addObservers(_MyObserverPointer o){
        if(o){
            synchronized lock(mObserverMutex);
            if(std::find(mObservers.begin(), mObservers.end(), o) == mObservers.end()){
                mObservers.push_back(o);
            }
        }
    }
    
    void deleteObservers(_MyObserverPointer o){
        if(o){
            synchronized lock(mObserverMutex);
            mObservers.remove(o);
        }
    }
    
    
    void notifyObservers(_ArgumentType&& arg){
        std::future<void> notifiyRes;
        
        {
            synchronized lock(mObserverMutex);
 
            
            /**
             * 여기서 호출하기에 적절한지 검사하고 모든 mObservers의 원소들을 복사하여 비동기로 호출한다.
             * 이때 새로 추가되거나 제거된 옵저버에 대해서는 신경쓰지 않는다.
             */
            if(mObservers.size() == 0){
                return;
            }
            
            try{
                notifiyRes = callObserverUpdateAsync(std::vector<_MyObserverPointer>(mObservers.begin(), mObservers.end()), std::move(arg));
            }catch(std::system_error&){}
            
        }
        
        if(notifiyRes.valid()){
            notifiyRes.get();
        }
    }
 
    inline void notifyObservers(){ notifyObservers(_ArgumentType()); }
    
private:
    
    /**
     * 한 옵저버에서 비동기 실행을 한다.
     * 실행 시에는 아래에 있는 callObserverUpdateAsync 로 실행한다.
     */
    auto callObserverUpdateAsync(_MyObserverPointer observers, typename std::add_lvalue_reference<const _ArgumentType>::type arg) -> std::future<void>{
        return std::async(std::launch::async,[=]{
            try{
                observers->OnUpdate(this, arg);
            }catch(...){
                
            }
        });
    }
    
    /**
     * [(이 함수에서 mObservers에 들어있는 함수들을 std::async 호출을 통해서
     * 비동기로 실행한다.
     * 실행후 모든 Observer의 실행이 끝날때 같이 종료한다.)]
     * - 위에 설명된 동작을 하는 비동기 작업을 반환한다.
     */
    auto callObserverUpdateAsync(std::vector<_MyObserverPointer> observers, _ArgumentType&& arg) -> std::future<void>{
        return std::async(std::launch::async,[=]{
            std::vector<std::future<void>> callResults;
            
            for(auto f : observers){
                try{
                    callResults.push_back(callObserverUpdateAsync(f, arg));
                }catch(std::system_error&){}
            }
            
            for(auto& r : callResults){
                if (r.valid()) {
                    r.get();
                }
            }
        });
    }
};
cs



3. 테스트 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using test_type = int;
std::mutex printLock;
 
class ObserverImpl : public Observer<test_type>{
    virtual void OnUpdate(dispatcher_type object, test_type argument) override{
        std::lock_guard<std::mutex> lock(printLock);
        cout <<"ObserverImpl val:" << argument <<endl;
    }
};
class ObserverImpl2 : public Observer<test_type>{
    virtual void OnUpdate(dispatcher_type object, test_type argument) override{
        std::lock_guard<std::mutex> lock(printLock);
        cout <<"ObserverImpl2 val:" << argument <<endl;
    }
};
 
void foo(){
    
}
int main(int argc, const char * argv[]) {
 
    Observable<test_type> o;
    
    o.addObservers(new ObserverImpl());
    o.addObservers(new ObserverImpl2());
    o.notifyObservers(3);
 
}
cs



각각의 호출은 따로 진행하기떄문에 lock이나 다른 처리를 해야 제대로 출력 결과를 볼수 있음 

'C++ 패턴' 카테고리의 다른 글

C++ 예외 처리  (0) 2017.10.18
옵저버 패턴 Observer Pattern [function]  (0) 2017.10.04
Comments