从基本指针列表循环(OOD)调用派生类方法

时间:2018-11-22 14:49:49

标签: c++ oop inheritance design-patterns polymorphism

问题

我遇到一个简单的问题,尽管我无法为此提出一个适当的OOD。

我所拥有的:

  • 基类
  • 子类添加新方法foo()
  • 指向基类实例的指针列表

我需要什么:

我需要遍历此列表,并为支持此方法的对象(即从上述子类(或从其派生)的对象)调用foo()。或一般来说,我需要通过指向基类的指针列表对子类进行“非卑鄙的”多态访问。

示例代码

class Entity {
    // ...
    // This class contains methods also needed by subclasses.
};

class SaveableEntity : public Entity {
public:
    virtual void save() = 0;
};

// SaveableEntity has multiple subclasses with specific save() implementations.

std::vector<Entity *> list;
for (Entity *entity : list) {
    // Here I need to save() the descendants of a SaveableEntity type.
}

我提出了一些想法,但是对我来说,这些想法都不对。以下是其中一些:

方法1:dynamic_cast

由于某些元素是可保存的,而有些元素则不可保存,所以我看到的最明显的方法是动态转换:

std::vector<Entity *> list;
for (Entity *entity : list) {
    auto saveable = dynamic_cast<SaveableEntity *>(entity);
    if (saveable) {
        saveable->save();
    }
}

但是,在这种情况下,使用dynamic_cast似乎是不好的OOD(如果我输入错了,请纠正我)。另外,这种方法很容易导致违反LSP

方法2:将save()移至基类

我可以删除SaveableEntity并将save()方法移至基础Entity。但是,这使我们实现了虚拟方法:

class Entity {
    virtual void save() {
        // Do nothing, override in subclasses
    }
};

这消除了dynamic_cast的使用,但是伪方法似乎仍然不正确:现在,基类保存的信息(save()方法)与之完全无关。

方法3:应用设计模式

  • 策略模式:SaveStrategy类及其子类,例如NoSaveStrategySomeSaveStrategySomeOtherSaveStrategy等。同样,{{ 1}}让我们回到了以前方法的缺陷:基类必须知道有关其子类的特定细节,这似乎是一个糟糕的设计。
  • 代理 Decorator 模式可以轻松地封装NoSaveStrategy,但是这只会隐藏不需要的代码,而不会摆脱不良的设计本身。 / li>
  • 添加一些合成上的继承层,依此类推...

问题

也许我缺少一些明显的解决方案,或者在这种特殊情况下,所描述的方法(1或2)并不像我所看到的那样糟糕和有臭味。

那么在这种情况下哪种设计方法合适?

2 个答案:

答案 0 :(得分:2)

解决方案#4受到面向数据编程的鼓励(在cppcon 2018上有一个很好的演讲,可在youtube上找到):有两个列表。一个列表用于所有SavableEntity,而另一个列表用于Entity,这些列表无法保存。

现在,您遍历第一个列表并->save()个项目。

主要优点是您仅对相关实体进行迭代。通过某些(可能是主要的)重构,您可以拥有对象的集合,而不是指向某些对象的指针。这样可以增加数据的局部性,并大大减少高速缓存未命中的次数。

答案 1 :(得分:0)

我的第一个想法是使用padding方法扩展基类。默认值可以为virtual bool trySave(),而return false;提供此替代:

SaveableEntity

对于您的特定情况,这是否比@YSC的建议更合适,您必须自己决定。这类似于将class SaveableEntity : public Entity { public: virtual bool trySave() final override { save(); return true; } // You could also make this protected. virtual void save() = 0; }; 移到基类中,但对用户的混乱明显减少。