处理不同派生 class 对象之间的交互

Handling interactions between different derived class objects

我有一个程序,其中有一个指向基 class 的指针向量,我在其中存储多个不同派生 class 的实例。所有这些对象根据它们的类型以不同的方式相互交互,我想以一种聪明的方式实现这些交互。我更希望有一个中介 class 来尽可能多地保存与这些交互相关的代码。

这是我想要实现的简化版本:

#include <iostream>
#include <vector>

class Gesture {};

class Rock : public Gesture {};

class Paper : public Gesture {};

class Scissors : public Gesture {};

class Game {
    public:
        static void play(Rock* first, Rock* second) {
            std::cout << "Rock ties with rock." << std::endl;
        }
        static void play(Rock* first, Paper* second) {
            std::cout << "Rock is beaten by paper." << std::endl;
        }
        static void play(Rock* first, Scissors* second) {
            std::cout << "Rock beats scissors." << std::endl;
        }
        static void play(Paper* first, Rock* second) {
            std::cout << "Paper beats rock." << std::endl;
        }
        static void play(Paper* first, Paper* second) {
            std::cout << "Paper ties with paper." << std::endl;
        }
        static void play(Paper* first, Scissors* second) {
            std::cout << "Paper is beaten by scissors." << std::endl;
        }
        static void play(Scissors* first, Rock* second) {
            std::cout << "Scissors are beaten by rock." << std::endl;
        }
        static void play(Scissors* first, Paper* second) {
            std::cout << "Scissors beat paper." << std::endl;
        }
        static void play(Scissors* first, Scissors* second) {
            std::cout << "Scissors tie with scissors." << std::endl;
        }
};

int main()
{   
    Rock rock;
    Paper paper;
    Scissors scissors;
    
    std::vector<Gesture*> gestures;
    gestures.push_back(&rock);
    gestures.push_back(&paper);
    gestures.push_back(&scissors);
    
    for(int i = 0; i < 3; ++i) {
        for(int j = 0; j < 3; ++j) {
            // alas, downcasting doesn't happen automagically...
            Game::play(gestures[i], gestures[j]);
        }
    }

    return 0;
}

有没有一种简单的方法可以在没有编译时知识的情况下实现向下转换,派生 classes 有问题?这个问题的公认答案: 似乎解决了我的问题,但我想知道我是否可以避免对派生的 classes 进行更改(我想将相关代码收集到调解器中class,如果可能的话)。我想我可以用一个 switch 语句找出派生的 class ,该语句试图向下转换为所有可能的派生 classes 并检查哪个不是 return a nullptr ,但这似乎不是特别“聪明”。

有什么想法吗?

我对这种情况的典型反应是在 enum 类型的基础 class 中添加一个字段。

typedef enum {rockType,paperType,scissorsType} fred;

class Gesture
{
    virtual fred Type() = 0;
}

class Rock : public Gesture
{
    fred Type() { return typeRock;}
}

等等。然后你可以调用每个对象的 Type() 函数并使用 switch 语句来调度每个对象。

因为我似乎无法避免使用丑陋的 switch 语句或 if-else if 链,我想我会做类似下面的事情我不需要将 enum 引入 类 或以其他方式修改它们。

#include <iostream>
#include <vector>

class Gesture {
    virtual void foo() {}
};

class Rock : public Gesture {
    virtual void foo() {}
};

class Paper : public Gesture {
    virtual void foo() {}
};

class Scissors : public Gesture {
    virtual void foo() {}
};

class Game
{
    public:
        static void rock_vs_rock() {
            std::cout << "Rock ties with rock." << std::endl;
        }
        static void rock_vs_paper() {
            std::cout << "Rock is beaten by paper." << std::endl;
        }
        static void rock_vs_scissors() {
            std::cout << "Rock beats scissors." << std::endl;
        }
        static void paper_vs_rock() {
            std::cout << "Paper beats rock." << std::endl;
        }
        static void paper_vs_paper() {
            std::cout << "Paper ties with paper." << std::endl;
        }
        static void paper_vs_scissors() {
            std::cout << "Paper is beaten by scissors." << std::endl;
        }
        static void scissors_vs_rock() {
            std::cout << "Scissors are beaten by rock." << std::endl;
        }
        static void scissors_vs_paper() {
            std::cout << "Scissors beat paper." << std::endl;
        }
        static void scissors_vs_scissors() {
            std::cout << "Scissors tie with scissors." << std::endl;
        }
        static void play(Gesture* first, Gesture* second)
        {
            // I've never really liked switch statements...
            if(dynamic_cast<Rock*>(first) && dynamic_cast<Rock*>(second))
            {
                rock_vs_rock();
            }
            else if(dynamic_cast<Rock*>(first) && dynamic_cast<Paper*>(second))
            {
                rock_vs_paper();
            }
            else if(dynamic_cast<Rock*>(first) && dynamic_cast<Scissors*>(second))
            {
                rock_vs_scissors();
            }
            else if(dynamic_cast<Paper*>(first) && dynamic_cast<Rock*>(second))
            {
                rock_vs_rock();
            }
            else if(dynamic_cast<Paper*>(first) && dynamic_cast<Paper*>(second))
            {
                paper_vs_paper();
            }
            else if(dynamic_cast<Paper*>(first) && dynamic_cast<Scissors*>(second))
            {
                paper_vs_scissors();
            }
            else if(dynamic_cast<Scissors*>(first) && dynamic_cast<Rock*>(second))
            {
                scissors_vs_rock();
            }
            else if(dynamic_cast<Scissors*>(first) && dynamic_cast<Paper*>(second))
            {
                scissors_vs_paper();
            }
            else if(dynamic_cast<Scissors*>(first) && dynamic_cast<Scissors*>(second))
            {
                scissors_vs_scissors();
            }
        }
};

int main()
{   
    Rock rock;
    Paper paper;
    Scissors scissors;
    
    std::vector<Gesture*> gestures;
    gestures.push_back(&rock);
    gestures.push_back(&paper);
    gestures.push_back(&scissors);
    
    for(int i = 0; i < gestures.size(); ++i)
    {
        for(int j = 0; j < gestures.size(); ++j)
        {
            Game::play(gestures[i], gestures[j]);
        }
    }

    return 0;
}