2012-08-24 32 views
6

Tôi đang thiết kế một mô hình quan sát viên mà nên làm việc theo cách này: quan sát gọi AddEventListener phương pháp EventDispatcher và vượt qua một chuỗi đó là tên của event, PointerToItselfPointerToItsMemberMethodC++ riêng Observer Pattern

Sau đó event xảy ra bên trong của EventDispatcher; nó xem qua danh sách đăng ký và nếu có, một số được gán cho sự kiện này gọi phương thức action của observer.

Tôi đã đến số này EventDispatcher.h. THẬN TRỌNG chứa bit mã giả.

Các hai câu hỏi:

  1. Làm thế nào để xác định các loại action trong struct Subscription?
  2. Tôi có đang di chuyển đúng không?

PS: Không, Tôi sẽ không sử dụng boost hoặc bất kỳ các thư viện khác.

#pragma once 

#include <vector> 
#include <string> 

using namespace std; 

struct Subscription 
{ 
     void*     observer; 
     string     event; 
     /* u_u */    action; 
}; 

class EventDispatcher 
{ 
    private: 
     vector<Subscription> subscriptions; 

    protected: 
     void     DispatchEvent (string event); 

    public: 
     void     AddEventListener (Observer* observer , string event , /* u_u */ action); 
     void     RemoveEventListener (Observer* observer , string event , /* u_u */ action); 
}; 

Tiêu đề này thực hiện như thế này trong EventDispatcher.cpp

#include "EventDispatcher.h" 

void EventDispatcher::DispatchEvent (string event) 
{ 
    int key = 0; 
    while (key < this->subscriptions.size()) 
    { 
     Subscription subscription = this->subscriptions[key]; 
     if (subscription.event == event) 
     { 
      subscription.observer->subscription.action; 
     }; 
    }; 
}; 

void EventDispatcher::AddEventListener (Observer* observer , string event , /* */ action) 
{ 
    Subscription subscription = { observer , event , action); 
    this->subscriptions.push_back (subscription); 
}; 

void EventDispatcher::RemoveEventListener (Observer* observer , string event , /* */ action) 
{ 
    int key = 0; 
    while (key < this->subscriptions.size()) 
    { 
     Subscription subscription = this->subscriptions[key]; 
     if (subscription.observer == observer && subscription.event == event && subscription.action == action) 
     { 
      this->subscriptions.erase (this->subscriptions.begin() + key); 
     }; 
    }; 
}; 
+2

Để xấu bạn' không sử dụng tăng, vì điều đó sẽ cho phép một giải pháp an toàn dễ dàng và loại sẽ tốt hơn cách tiếp cận hiện tại của bạn và linh hoạt hơn. Các giải pháp C++ 11 có được phép không? – Ylisar

+0

Tôi chưa thực sự biết, C++ 11 là gì ... Đó là một tiêu chuẩn mới, đúng không? Tôi tự hỏi, nếu 'g ++' của tôi biết nó chưa? Tiêu chuẩn mới là ok để sử dụng, nó không phải là một thư viện ... – Kolyunya

Trả lời

1

Có lẽ bạn chỉ nên tạo một lớp học để được bắt nguồn bởi "người sử dụng":

class Action { 
    public: 
     friend class EventDispatcher; 

     virtual SomeResultType DoThis() = 0; 

    private: 
     /* Some common data */ 
}; 

Chỉ cần vượt qua một số có nguồn gốc-từ-lớp-Action gõ biến để addEventListener. Khi sự kiện tương ứng được kích hoạt, chỉ cần điền vào dữ liệu chung và gọi phương thức DoThis().

void EventDispatcher::DispatchEvent (string event) 
{ 
    int key = 0; 
    while (key < this->subscriptions.size()) 
    { 
     Subscription subscription = this->subscriptions[key]; 
     if (subscription.event == event) 
     { 
      subscription->action(); 
     }; 
    }; 
}; 

Đối addEventListener:

void EventDispatcher::AddEventListener (Observer* observer , string event , Action* action) 
{ 
    Subscription subscription = { observer , event , action); 
    this->subscriptions.push_back (subscription); 
}; 

Một ví dụ về một lớp học có nguồn gốc Hành động:

class myAction: public Action { 
    public: 
     // Implement the DoThis() method 
     void SomeResultType DoThis() { 
      cout << "Hello World!"; 
      return SomeValue; 
     } 
}; 

// To use the action, 
myAction* act = new myAction; 
myEventDispatcher.AddEventListener(someObserver, "HelloWorld", act); 

Đây là một trong những cách an toàn nhất để thực hiện hành động (và callbacks).

+0

Tôi có thể chuyển đến 'AddEventListener' một con trỏ tới một hàm thành viên của' observer' và gọi nó từ 'EventDispatcher'? Tôi làm nó như thế nào? Cảm ơn bạn? – Kolyunya

+0

Xin lỗi vì sự chậm trễ. Vui lòng tránh sử dụng con trỏ hàm. Trong phương thức DispatchEvent() của bạn, bạn chỉ có thể có hành động-> DoThis(); –

+0

Tôi xin lỗi, nhưng tôi không hiểu ... Hãy nói, người quan sát có một phương pháp không tĩnh 'DoSmth()'. Làm cách nào để truyền phương thức này cho 'EventDispatcher' và' EventDispatcher' sẽ gọi phương thức này sau này như thế nào? – Kolyunya

1

Trong dạng đơn giản nhất u_u có thể là một con trỏ hàm ví dụ

typedef void (*u_u)(void*); // or whatever arguments u like 

thì bạn chỉ cần cung cấp chức năng được gọi bất cứ khi nào sự kiện được kích hoạt.

void myaction(void* arg) 
{ 
    ... 
} 

Subscription s; 
... 
s.action = myaction; 
3

Bạn có thể xác định lớp Hành động hoặc chuyển một hàm lambda (C++ 11).Trong trường hợp sau, hành động có thể được định nghĩa là

function<void (EventDispatcher*)> action; 

và bạn sẽ đăng ký người quan sát như sau

Observer * me = this; 
observable->AddEventListener (this, "EventName", [me] (EventDispatcher* dispatcher) { 
    // code here; me is available 
}); 

Bạn có lẽ nên sử dụng con trỏ yếu thông minh để lưu trữ các nhà quan sát trong EventDispatcher, chẳng hạn mà bạn không phải quan tâm đến việc không đăng ký.

Edit: thêm ví dụ sau (chỉ là một thuê bao có thể, nhưng nên minh họa cho ý tưởng - bạn phải cẩn thận mà bạn không tham chiếu đến một đối tượng mà không còn tồn tại)

struct Observable { 
    std::weak_ptr<function<void (const Observable&)>> action; 

    void AddEventListener (std::weak_ptr<function<void (const Observable&)>> theAction) { 
     action = theAction; 
    } 

    void EventRaised() { 
     if (!action.expired()) { 
     auto theAction = action.lock(); 
     (*theAction) (*this); 
     } 
    } 
}; 

struct Observer { 
... 
    void CallOnEvent (const Observable & observable) { 
     // do something 
    } 

    // field to store the action as long as it is needed 
    std::shared_ptr<function<void (const Observable&)>> action; 

    void ... { 
     auto me = this; 
     action = std::make_shared<function<void (const Observable&)>> (
     [me] (const Observable& observable) { 
      me->CallOnEvent (observable); 
     } 
    ); 
     // we could have as well used std::bind 
     observable.AddEventListener (action); 
    } 
}; 
+0

Tôi có thể chuyển đến 'AddEventListener' một con trỏ tới một hàm thành viên của người quan sát và gọi nó từ' EventDispatcher'? Tôi làm nó như thế nào? Cảm ơn bạn? – Kolyunya

+0

@Kolyunya chỉ cần chuyển 'std :: bind (Observer :: whateverMethod, me)' thành hàm thay vì lambda. –

+0

Tôi chỉ không nhận được mã của bạn ... Nó phải là tiêu chuẩn mới C++ 11, tôi chưa biết ... Cảm ơn và xin lỗi ... – Kolyunya