在访问者模式

时间:2017-04-21 21:59:48

标签: c++ crtp visitor visitor-pattern

我正在尝试使用模板,访问者模式和CRTP帮助编写消息传递系统。我理解这些概念,但我仍处于一种情况,我必须回顾“丢失”类型。我有一个Base课程,我想找一个Derived<T>。推断[Derived的“两种”类型可能是任何东西,T可以是任何东西](即使它被视为一种类型)。

我试图利用第二个访客模式,这似乎很重,很疯狂,但我找不到任何有效的解决方案。即使它与游戏相关,也只是作为一个例子,它可以应用于我想的其他程序,我不能在另一个环境中公开它。

这是我使用的命名(带有不必要的例子):

  • SubscriberBase是一个发送和接收消息的类(如网络客户端)
  • Broadcaster是订阅者之间的桥梁(如网络交换机/服务器),包含SubscriberBase的向量。

我想出了这个最小的代码:

class SubscriberBase {};

class Broadcaster {
    std::vector<SubscriberBase*> subscribers;
public:
    template<typename TMessage>
    void broadcast(TMessage& message) {
        for(auto subscriber : subscribers) {

            // <<< Here is my problem <<<
            message.accept<THE_ACTUAL_SUBSCRIBER_TYPE>(subscriber);

        }
    }

    void attach(SubscriberBase* subscriber) {
        subscribers.push_back(subscriber);
    }
};

//Base class for handling messages of any type

template<typename TMessage>
class MessageHandler {
public:
    virtual void handleMessage(TMessage& message) {}
};

//Base class for messages

template<typename TMessage>
class Message {
    friend class Broadcaster;
private:

    //Visitor pattern with CRTP
    template<typename TSubscriber>
    void accept(TSubscriber* subscriber) {
        subscriber->handleMessage(*static_cast<TMessage*>(this));
    }

};

//Messages

struct EntityCreated : public Message<EntityCreated> {};
struct EntityDestroyed : public Message<EntityDestroyed> {};
struct BurnAllGummyBears : public Message<BurnAllGummyBears> {};

//Subscribers

class EntityCache : public SubscriberBase,
    public MessageHandler<EntityCreated>,
    public MessageHandler<EntityDestroyed>,
    public MessageHandler<BurnAllGummyBears>
{
public:
    void handleMessage(EntityCreated& message) override { /* add to cache */ }
    void handleMessage(EntityDestroyed& message) override { /* remove from cache */ }
    //does not override BurnAllGummyBears because it's not relevant for EntityCache
};

问题是类型THE_ACTUAL_SUBSCRIBER_TYPE。它可以是任何“具体”用户;在这种情况下,它将是EntityCache或其他类似LoggerCommandRecorder ......

我尝试使用另一个CRTP和另一个类:

class SubscriberBase {};

template<typename TSubscriber>
class Subscriber : public SubscriberBase { /* some other visitor pattern ? */ };

class EntityCache : public Subscriber<EntityCache>, /* ... */

没有成功。

所有的想法都表示赞赏,谢谢:)

1 个答案:

答案 0 :(得分:0)

我放弃了这个想法并且想“好吧,亲我的......”所以我决定采用更简单的设计,正常而干净的方法来获得正常而干净的代码。

//Messages
struct EntityCreated {};
struct EntityDestroyed {};
struct ChopAllTrees {};
struct MakeGummyBearsEvil {};

//Subscriber is now a base class containing all of the message handlers
class Subscriber {
public:
    virtual void handleMessage(EntityCreated& msg) {}
    virtual void handleMessage(EntityDestroyed& msg) {}
    virtual void handleMessage(ChopAllTrees& msg) {}
    virtual void handleMessage(MakeGummyBearsEvil& msg) {}
};

class Broadcaster {
    std::vector<Subscriber*> subscribers;
public:    
    template<typename M>
    void broadcast(M& msg) {
        for(auto subscriber : subscribers) {
            subscriber->handleMessage(msg);
        }
    }

    template<typename M>
    void broadcast(M&& msg) {
        M owner(msg);
        broadcast(owner);
    }

    void attach(Subscriber* subscriber) {
        auto it = std::find(subscribers.begin(), subscribers.end(), subscriber);
        if(it == subscribers.end()) {
            subscribers.push_back(subscriber);
        }
    }

    void detach(Subscriber* subscriber) {
        auto it = std::find(subscribers.begin(), subscribers.end(), subscriber);
        if(it != subscribers.end()) {
            subscribers.erase(it);
        }
    }
};

//Subscribers simply inherits from Subscriber and overrides interesting handlers
class EntityCache : public Subscriber {
    void handleMessage(EntityCreated&   msg) override {
        std::cout << "Handling creation\n";
    }
    void handleMessage(EntityDestroyed& msg) override {
        std::cout << "Handling destruction\n";
    }
};

class Eviler : public Subscriber {
    void handleMessage(MakeGummyBearsEvil& msg) override {
        std::cout << "Gummy bears are now evil!\n";
    }
};

int main() {
    EntityCache entityCache;
    Eviler eviler;

    Broadcaster broadcaster;
    broadcaster.attach(&entityCache);
    broadcaster.attach(&eviler);

    EntityCreated entityCreated;

    broadcaster.broadcast(entityCreated); //lvalue test
    broadcaster.broadcast(MakeGummyBearsEvil()); //rvalue test

    broadcaster.detach(&eviler);

    broadcaster.broadcast(EntityDestroyed());
    broadcaster.broadcast(MakeGummyBearsEvil()); //no effect
}

我接受了这个答案,因为它解决了这个问题,但由于我仍然对如何做到这一点感兴趣,如果有人在过去遇到这个问题(可能没有)并处理它,请随时回答我我会接受的。