仅持有父类Entity列表时,如何用函数重载处理不同子类?
解决方案
针对你的问题,有两种符合OOP原则的常用解决方案,既能避免类型判断,又能正确触发对应的绘制逻辑:
方案一:利用反向调用的多态(将绘制入口移至实体类)
这种方案通过在父类Entity中定义纯虚的绘制接口,由子类实现并反向调用Painter的重载方法,实现正确的函数分发。
代码修改示例:
- 调整
Entity类,添加纯虚方法并前向声明Painter:
class Painter; class Entity { public: virtual ~Entity() = default; // 子类必须实现该方法,触发对应的绘制逻辑 virtual void draw(Painter* painter) = 0; };
- 每个实体子类实现
draw方法,调用Painter的重载:
class EntityType1 : public Entity { public: void draw(Painter* painter) override { // 传入当前子类指针,触发Painter中对应的重载draw painter->draw(this); } }; class EntityType2 : public Entity { public: void draw(Painter* painter) override { painter->draw(this); } };
Painter保持原有重载,并修改遍历逻辑:
class Painter { public: void draw(EntityType1* entity) { // EntityType1的具体绘制逻辑 } void draw(EntityType2* entity) { // EntityType2的具体绘制逻辑 } // 其他实体类型的重载... void draw(Scene* scene) { std::list<Entity*> drawables = scene->getDrawables(); for (auto entity : drawables) { // 调用子类的draw方法,间接触发对应重载 entity->draw(this); } } };
优势:
- 实现简单,符合开闭原则:新增实体类型只需添加子类并实现
draw方法,无需修改Painter的遍历逻辑。 - 逻辑清晰,每个实体明确自己的绘制入口。
方案二:访问者模式(双重分发)
如果需要将绘制逻辑与实体类完全解耦,或者后续会新增更多类似的操作(如碰撞检测、序列化),访问者模式是更合适的选择。它通过双重分发机制,同时确定实体类型和操作类型。
代码修改示例:
- 定义访问者接口:
// 前向声明所有实体子类 class EntityType1; class EntityType2; class EntityType3; class EntityVisitor { public: virtual ~EntityVisitor() = default; // 每个实体类型对应一个visit方法 virtual void visit(EntityType1* entity) = 0; virtual void visit(EntityType2* entity) = 0; virtual void visit(EntityType3* entity) = 0; };
- 修改
Entity类,添加接受访问者的接口:
class Entity { public: virtual ~Entity() = default; virtual void accept(EntityVisitor* visitor) = 0; };
- 实体子类实现
accept方法,将自身传递给访问者:
class EntityType1 : public Entity { public: void accept(EntityVisitor* visitor) override { visitor->visit(this); } }; class EntityType2 : public Entity { public: void accept(EntityVisitor* visitor) override { visitor->visit(this); } };
Painter实现访问者接口,并修改遍历逻辑:
class Painter : public EntityVisitor { public: void visit(EntityType1* entity) override { // EntityType1的绘制逻辑 } void visit(EntityType2* entity) override { // EntityType2的绘制逻辑 } // 其他实体类型的visit实现... void draw(Scene* scene) { std::list<Entity*> drawables = scene->getDrawables(); for (auto entity : drawables) { entity->accept(this); } } };
优势:
- 完全解耦实体与操作:绘制逻辑集中在
Painter中,实体类无需感知具体操作。 - 扩展性强:新增操作只需新增访问者实现类,无需修改实体类。
注意:
- 如果后续频繁新增实体类型,需要修改访问者接口和所有实现类,维护成本会上升。
内容的提问来源于stack exchange,提问作者Andre Urzua
相关产品推荐
相关产品推荐

