2012-04-02 33 views
6

Tôi đang cố gắng triển khai bus sự kiện an toàn. Tôi bị kẹt với hàm EventBus::subscribe vì nó không chấp nhận xử lý sự kiện cụ thể của tôi. Trong một phiên bản cũ hơn, tôi chỉ có AbstractEventHandler được triển khai như một lớp trừu tượng, không có nó là mẫu. Tôi không có vấn đề với việc thực hiện đó. Đó là lý do tại sao tôi giả định rằng vấn đề thực tế là với mẫu trừu tượng.Chức năng C++ không chấp nhận thực hiện cụ thể

Mã bên dưới là phiên bản thực hiện của tôi. Khối đầu tiên bao gồm "bộ xương" của bus sự kiện và các lớp cần thiết của nó trong khi khối thứ hai cho thấy việc thực hiện thực tế của sự kiện, trình xử lý sự kiện và phần chính.

enum lưu giữ tất cả các sự kiện khác nhau có sẵn. Sự kiện trừu tượng là cơ sở mà tất cả các sự kiện cụ thể đều bắt nguồn từ. Trình xử lý sự kiện là một khuôn mẫu trừu tượng với một sự kiện làm lớp mẫu để đảm bảo an toàn kiểu. Bus sự kiện có trách nhiệm phân phối tất cả các sự kiện đã được công bố cho các trình xử lý tương ứng của nó.

enum EVENT_TYPE 
{ 
    ON_EVENT_1, 
    ON_EVENT_2 
}; 

class AbstractEvent 
{ 
public: 
    AbstractEvent() {}; 
    virtual ~AbstractEvent() {}; 

    virtual EVENT_TYPE type() = 0; 
}; 

template<class T> 
class AbstractEventHandler 
{ 
public: 
    AbstractEventHandler() {}; 
    virtual ~AbstractEventHandler() {}; 

    virtual void on_event(T *event) = 0; 
}; 

class EventBus 
{ 
public: 
    EventBus() {}; 
    virtual ~EventBus() {}; 

    void subscribe(EVENT_TYPE type, 
        AbstractEventHandler<AbstractEvent> *eventHandler) { 
     // Add handler to vector for further use 
    } 

    void publish(AbstractEvent *event) { 
     // send event to each handler in respective vector 
    } 
}; 

Dưới đây là cụ thể sự kiện và xử lý sự kiện của tôi và chính()

class ConcreteEvent : public AbstractEvent 
{ 
public: 
    ConcreteEvent() {}; 
    virtual ~ConcreteEvent() {}; 

    EVENT_TYPE type() { 
     return ON_EVENT_1; 
    }; 
}; 

class ConcreteEventHandler : public AbstractEventHandler<ConcreteEvent> 
{ 
public: 
    ConcreteEventHandler() {} 
    virtual ~ConcreteEventHandler() {}; 

    void on_event(ConcreteEvent *event) { 
     // Do something 
    }; 
}; 

int main() 
{ 
    EventBus *eventBus = new EventBus(); 

    ConcreteEventHandler handler = ConcreteEventHandler(); 

    // This failes! 
    eventBus->subscribe(ON_EVENT_1, &handler); 
} 

Lợi nhuận biên dịch với một lỗi nói rằng không có chức năng phù hợp cho cuộc gọi đến

EventBus::subscribe(EVENT_TYPE, ConcreteEventHandler*) 

và rằng các ứng cử viên duy nhất là

void EventBus::subscribe(EVENT_TYPE, AbstractEventHandler<AbstractEvent>*) 

Làm cách nào để triển khai EventBus :: phương thức đăng ký để chấp nhận triển khai cụ thể lớp trừu tượng của tôi?

Cập nhật: Giải pháp

Tôi đã thay đổi mô tả phương pháp EventBus::subscribe như sau và bây giờ công trình độc đáo:

template<typename T> 
void subscribe(EVENT_TYPE type, AbstractEventHandler<T> *eventHandler) { 

} 

Cảm ơn, Rohan, cho gợi ý của bạn! Họ đã giúp tôi tìm ra giải pháp này.

+0

+1 cho câu hỏi hay. –

Trả lời

5

Lý do là vì, ConcreteEventHandler là một lớp con của AbstractEventHandler<ConcreteEvent> và không AbstractEventHandler<AbstractEvent>.

Điều này có vẻ đáng ngạc nhiên, nhưng AbstractEventHandler<ConcreteEvent> không được là lớp con của AbstractEventHandler<AbstractEvent> mặc dù ConcreteEvent là một phân lớp của AbstractEvent.

Lý do là vì, với các mẫu, khuôn mẫu như bạn muốn không đảm bảo an toàn loại. Chúng ta hãy xem xét một ví dụ. Hãy xem qua mô hình chuẩn của một lớp cơ sở Animal và các lớp con CatDog.Hãy nói rằng chúng tôi có một danh sách các động vật:

std::list<Animals>* animals; 

và một danh sách các mèo:

std::list<Cat> cats; 

Sau đây, là không một dàn diễn viên hợp lệ:

animals = &cats; 

Lý do là, bởi vì , nếu tôi muốn làm điều này,

animals->add(new Dog("Ben")); 

Tôi wo uld thực sự thêm Dog vào danh sách Cat s. cats.last() ở đây thực sự sẽ trả lại Dog. Vì vậy, trong trường hợp này, về cơ bản bạn đang thêm Dog vào danh sách Cat s. Tôi đã nhìn thấy đủ Looney Tunes tập để biết rằng đây không phải là một ý tưởng tốt:

cats.last().meow(); 

Trên đây chắc chắn là không đúng sự thật, như chúng ta đều biết rằng một Dog chỉ có thể bowbow().

EDIT

Để trả lời câu hỏi của bạn, đây là những gì tôi đề nghị bạn làm; Để ConcreteEventHandler kế thừa từ AbstractEventHandler<AbstractEvent> và trong mã, bất cứ nơi nào bạn sử dụng ConcreteEvent, hãy sử dụng dynamic_case để truyền AbstractEvent thành ConcreteEvent. Điều này sẽ sử dụng thời gian chạy introspection, có thể ảnh hưởng đến hiệu suất một chút (tôi cũng đã thấy khá một vài người phản đối bằng cách sử dụng một diễn viên năng động), nhưng bạn sẽ có thể thực hiện thành công một upcast hợp lệ của datatype.

+0

Được rồi, đúng, nhưng nó không trả lời câu hỏi - Làm cách nào tôi có thể triển khai EventBus :: phương thức đăng ký của mình để chấp nhận triển khai cụ thể lớp trừu tượng của tôi? –

+0

Cảm ơn bạn đã trả lời nhanh chóng! Tôi hiểu vấn đề này tốt hơn bây giờ. Vấn đề là tôi cần AbstractEventHandler trong oder để buộc các trình xử lý cụ thể thực hiện đúng phương thức on_event(). – Fabian

+1

Bạn có giống như một người không? –

-2

Lớp học AbstractEventHandler<T> của bạn phải kế thừa AbstractEvent. Đây có lẽ là ý định của bạn, bạn chỉ quên viết nó.

template<class T> 
class AbstractEventHandler 
    :public AbstractEvent 
{ 
public: 
    AbstractEventHandler() {}; 
    virtual ~AbstractEventHandler() {}; 

    virtual void on_event(T *event) = 0; 
} 
+1

-1 không chỉ vì sai, mà còn không cố biên dịch mã. –

+0

ngữ nghĩa, người tổ chức sự kiện không được kế thừa sự kiện. Nó không phải là một sự kiện, đó là những gì đang xử lý sự kiện đó. – stefaanv

1

Rohan đã trả lời lý do tại sao mã không biên dịch, tuy nhiên tôi muốn đề xuất một cách tiếp cận khác.

Bạn có thể triển khai theo cách mà Eventhandler đăng ký trực tiếp với EventGenerator. Bằng cách này, có một liên kết trực tiếp giữa việc tạo và xử lý sự kiện.
Sự kiện sau đó sẽ giữ tham chiếu đến trình tạo của nó để cho phép nó truy cập trình xử lý được đăng ký và eventbus gọi một phương thức trên sự kiện để cho phép nó xử lý chính nó.

Bằng cách này, eventbus không biết về eventhandler và bạn thậm chí không cần enum eventtype.
Tuy nhiên, bạn cần các trình tổ chức sự kiện khác nhau mà các trình tổ chức sự kiện khác nhau có thể truy cập mà không cần đến một eventbus. Mỗi người tổ chức sự kiện chỉ có thể xử lý một sự kiện, vì vậy nếu cần thêm sự kiện, người tổ chức sự kiện phải được tổng hợp (theo ủy quyền hoặc kế thừa).

+0

Cảm ơn bạn đã đề xuất!Tôi không muốn đi dọc theo tuyến đường này vì hai lý do: 1/Xe buýt sự kiện là một phần của API và tôi không muốn để lộ các lớp học tạo sự kiện. 2/Khi dự án phát triển, tôi lo sợ sẽ kết thúc trong một mạng không thể kiểm soát các kết nối giữa các trình xử lý và trình tạo sự kiện. – Fabian

+0

Đủ công bằng, đó là dự án của bạn. Khoảng 2 /, rủi ro được giảm thiểu bằng cách tiếp cận theo lớp, trong đó các mô đun cao hơn biết các mô đun thấp hơn và có thể đăng ký và nơi các sự kiện chỉ được gửi từ các mô-đun thấp hơn đến cao hơn. – stefaanv

Các vấn đề liên quan