我需要为一个小游戏管理蚂蚁和殖民地(实际上是为了实验)。
我有一个Element类,它定义了游戏中的所有实体(蚂蚁,殖民地,食物和其他东西......)
所有其他课程都来自这一课程。
我的问题:
我有一个班级来管理所有实体。玩家可以选择他想要的东西。存储选定的实体:Element* selection;
如果选择的intity是Ant,则玩家可以移动它。但是,因为选择变量是一个Element指针,所以显然不能调用Ant类中的move()
方法。
我考虑测试的内容:
如果我实现一个名为isMovable()
的Element方法,它返回true或false,如果选择是可移动的,我会将它转换为Ant?我不知道什么是正确的解决方案。
我的移动方法:
void Manager::movementEvent(sf::Vector2i mPosition)
{
sf::Vector2f mousePosition = sf::Vector2f((float)mPosition.x, (float)mPosition.y);
if(this->selection) {
// I need to move the selected Ant
}
}
感谢您的帮助!!
修改
这是我的实际设计:
class Element {
private:
sf::Vector2f position;
int width, height;
public:
Element();
Element(sf::Vector2f position, int width, int height);
Element(const Element & element);
virtual ~Element();
};
class Colony: public Element {
private:
int capacity;
Queen *queen;
public:
Colony();
Colony(sf::Vector2f position, int width, int height, int capacity, Queen &queen);
Colony(Colony const & colony);
virtual ~Colony();
Colony& operator=(Colony const& colony);
};
class Ant: public Element
{
private:
sf::Vector2f destination;
int number, age, speed;
public:
Ant();
Ant(sf::Vector2f position, int number, int age, int width, int height, int speed);
Ant(const Ant & ant);
virtual ~Ant();
Ant& operator=(Ant const& ant);
};
class Manager {
private:
std::vector<Element*> ants;
std::vector<Element*> colonies;
Element* selection;
std::vector<Ant*> movement;
public:
Manager();
virtual ~Manager();
std::vector<Element*> getAnts();
std::vector<Element*> getColonies();
void addAnt(Ant* ant);
void addColony(Colony* colony);
void removeAnt(Ant* ant);
void removeColony(Colony* colony);
void draw(sf::RenderWindow * window);
void drawElement(sf::RenderWindow * window, std::vector<Element*> vector);
void selectionEvent(sf::Vector2i mousePosition);
bool checkSelection(sf::Vector2f mousePosition, std::vector<Element*> vector);
void movementEvent(sf::Vector2i mousePosition);
};
答案 0 :(得分:3)
我宁愿避免一般的设计,因为它最让我觉得是强迫性的。
基类应该定义许多派生类之间通用的行为,并为该常见行为提供通用接口。但是,在这种情况下,我觉得你的派生类实际上没有几乎没有共同的行为,所以你在它们之间的有用的公共接口方面几乎没有任何东西。
在这种情况下,你可能会因为强迫他们从一个(基本无意义的)“实体”类派生出来而失去更多的东西。事实上,我建议你几乎在任何时候你发现自己都在考虑一个类名称一般的“对象”或“实体”而不是暗示一组有意义的行为,那么你很有可能试图把不真正属于一起的东西推到一起。
所有这一切,如果你确实坚持要做到这一点,我会坚持一个基本的格言,最好说而不是问。因此,我在基类中定义了try_to_move
(或者可能只是将它命名为move
),但提供了一个失败的默认定义。然后覆盖Ant类中的实际移动。
class Entity {
// ...
virtual bool move_to(Location new_location) {
return false;
}
};
class Ant : public Entity {
// ...
virtual bool move_to(Location new_location) {
my_location = new_location;
return true;
}
};
通过这种方式,您可以告诉从Entity
派生的任何 - 但如果您告诉Food
对象移动,它就会失败。这大大简化了调用代码。而不是像这样的模式:
if (object->can_move()) {
if (object->move_to(new_location))
// succeeded
else
// failed
}
我们得到的代码如下:
if (object->move_to(new_location))
// succeeded
else
// failed
至少在一个典型的情况下,即使我们告诉蚂蚁移动,我们也可能最终处理失败的可能性,因此在要求对象之前添加询问对象是否可以移动的元素无论如何,这样做真的让我们无所适从。
根据具体情况,可能想要稍微更改代码,因此无法移动的不同原因会返回不同的错误代码,因此当/如果失败,您可以理清原因。或者,您可能更喜欢编写代码,以便它在移动时成功,或者抛出。在这些情况下(你宁愿在至少部分时间内失败)这可能不是最好的选择,但可能仍然值得考虑。
然而,我会重申,我认为一个更好的设计可能只是让Ant
和Food
分开,所以很容易将食物作为食物,蚂蚁作为蚂蚁,并且无需在运行时进行整理,无论是Food还是Ant,都知道如何与它进行交互。
答案 1 :(得分:1)
这真的闻起来像是在解决错误的问题。您将能够使用isMovable
等标记和强制转换来使其工作,但您的代码可能会变得一团糟,让您头疼。
也许你的问题实际上是
“我有一个班级来管理所有实体”
如果它们没有任何关联,它们可能不应该表达与实体的Is-A关系。如果每种类型都有不同的容器,它可能会更清洁。如何将用户想要的操作与“实体”联系起来将是另一回事。
答案 2 :(得分:1)
您可以在基类上添加virtual
方法move()
,而不是仅为Ant
类实现,因此当检查Element
时,class Element
{
public:
Element(bool movable) : m_movable(movable) {}
virtual void move() {};
bool isMovable() const { return m_movable; }
private:
bool m_movable;
};
class Ant : public Element
{
public:
Ant() : Element(true) {}
void move() { /* move */ }
};
class Food : public Element
{
public:
Food() : Element(false) {}
};
是可移动的,应该动:
move()
这样,每个派生类都有一个bool m_movable
方法,但它是从基类继承的(所以它留空)。
Occam的剃刀告诉我们,在这种情况下,您也不需要class Element
{
public:
Element() {}
virtual void move() {};
};
class Ant : public Element
{
public:
Ant() {}
void move() { /* move */ }
};
class Food : public Element
{
public:
Food() {}
};
标志,因此该片段简化了:
{{1}}