不,绝对没有标准模式。
我的建议是避免初学者面向对象设计的陷阱,即试图为所有东西找到一个单一的共同“基类”接口。你的龙和巫师大不相同,除了可爱的代码抽象之外,试图合并这两个接口没有任何好处。记住,继承不是代码重用的工具。
最简单的方法是让每个对象维护自己的内部状态(正如你已经描述的那样),并能够绘制该状态。这就是一个老派的 switch 语句可能比使用多态获得相同效果更清晰的地方。例如:
class Dragon {
enum State { asleep, flying, breathing_fire };
public:
void draw () {
switch (cur_state){
case asleep: draw_asleep ();
case flying: draw_flying ();
case breathing_fire: draw_breathing_fire();
}
}
private:
void draw_asleep ();
void draw_flying ();
void draw_breathing_fire();
};
有经验的C++开发人员会对上面的代码感到惊讶(他们应该这样做),因为switch语句几乎完全可以实现多态方法调用——运行时分派。(甚至更加神秘:一张方法地址表。)我个人认为,对于这种特定类型的类,即封装状态机的单个公共接口,switch语句更清晰、更易维护,因为它使状态机跳转明确。我认为,我也清楚地意识到这通常是不好的C++设计。
这种设计的摩擦是由状态机和单个公共接口之间的分界线引起的。一个正在睡觉的龙显然不同于一个飞行的龙。事实上,它们几乎没有任何共同点。但是高层设计说这两个是相同的龙。真是一团糟!你为每个状态创建不同的Dragon对象吗?不,因为那会向调用者暴露状态概念。你为每个状态创建不同的内部辅助对象并将其分派给它们?可能是,但很快就会变得混乱。上述方法是两者之间的妥协。把switch语句看作是隔离丑陋的方式。公共接口干净,私有接口也是如此。只有switch语句包含肮脏的混乱。考虑这种方法,也考虑其他建议。
最后,为了绘制您的向导和龙,一个简单的包装模板或函数对象就足够了:
struct Draw {
template <class S>
void operator()(S& s) { s.draw (); }
};
重点是,您不必合并两个不同的类,只因为它们都支持相同的逻辑操作。