主要问题很简单,真的。给定一个基类(更抽象的)和多个需要相互交互的派生类,你如何去做呢?
举一个更具体的例子,这里是一个 2d 视频游戏的 hitboxes 实现:
#include <stdio.h>
#include <vector>
#include "Header.h"
bool Hitbox::isColliding(Hitbox* otherHtb) {
printf("Hitbox to hitbox.\n");
return this->isColliding(otherHtb);
}
bool CircleHitbox::isColliding(Hitbox* otherHtb) {
printf("Circle to hitbox.\n");
// Try to cast to a circle.
CircleHitbox* circle = dynamic_cast<CircleHitbox*>(otherHtb);
if (circle) {
return this->isColliding(circle);
}
// Try to cast to a square.
SquareHitbox* square = dynamic_cast<SquareHitbox*>(otherHtb);
if (square) {
return this->isColliding(square);
}
// Default behaviour.
return 0;
}
bool CircleHitbox::isColliding(CircleHitbox* otherHtb) {
printf("Circle to circle.\n");
// Suppose this function computes whether the 2 circles collide or not.
return 1;
}
bool CircleHitbox::isColliding(SquareHitbox* otherHtb) {
printf("Circle to square.\n");
// Suppose this function computes whether the circle and the square collide or not.
return 1;
}
// This class is basically the same as the CircleHitbox class!
bool SquareHitbox::isColliding(Hitbox* otherHtb) {
printf("Square to hitbox.\n");
// Try to cast to a circle.
CircleHitbox* circle = dynamic_cast<CircleHitbox*>(otherHtb);
if (circle) {
return this->isColliding(circle);
}
// Try to cast to a square.
SquareHitbox* square = dynamic_cast<SquareHitbox*>(otherHtb);
if (square) {
return this->isColliding(square);
}
// Default behaviour.
return 0;
}
bool SquareHitbox::isColliding(CircleHitbox* otherHtb) {
printf("Square to circle.\n");
// Suppose this function computes whether the square and the circle collide or not.
return 1;
}
bool SquareHitbox::isColliding(SquareHitbox* otherHtb) {
printf("Square to square.\n");
// Suppose this function computes whether the 2 squares collide or not.
return 1;
}
int main() {
CircleHitbox a, b;
SquareHitbox c;
std::vector<Hitbox*> hitboxes;
hitboxes.push_back(&a);
hitboxes.push_back(&b);
hitboxes.push_back(&c);
// This runtime polymorphism is the subject here.
for (Hitbox* hitbox1 : hitboxes) {
printf("Checking all collisions for a new item:\n");
for (Hitbox* hitbox2 : hitboxes) {
hitbox1->isColliding(hitbox2);
printf("\n");
}
}
return 0;
}
与头文件:
#pragma once
class Hitbox {
public:
virtual bool isColliding(Hitbox* otherHtb);
};
class CircleHitbox : public Hitbox {
public:
friend class SquareHitbox;
bool isColliding(Hitbox* otherHtb) override;
bool isColliding(CircleHitbox* otherHtb);
bool isColliding(SquareHitbox* otherHtb);
};
class SquareHitbox : public Hitbox {
public:
friend class CircleHitbox;
bool isColliding(Hitbox* otherHtb) override;
bool isColliding(CircleHitbox* otherHtb);
bool isColliding(SquareHitbox* otherHtb);
};
我对此的主要问题是每个派生类都需要在重写函数中进行的“is-a”检查。
我看到的替代方案是访问者设计模式,但这可能:
对于这个看似简单的问题来说太复杂了。
导致的问题多于解决方案。
该代码应该保留的一个特性是,没有派生类被强制实现与每个(或任何其他派生类)的交互。另一个是能够将所有派生对象存储在基本类型数组中而无需任何对象切片。