我正在开发一个具有一些功能的类,我认为以后可能需要扩展这些功能,但不是现在。如果要扩展这个类,那么我认为这将使实例化基类变得毫无意义。
例如,假设我的基类是一棵树。一种方法是将树为我的目的所需的所有操作都放在tree类中,并将其留在此处。但是,这个树在以后的生活中可能对程序的其他方面很有用,所以我考虑创建一个纯虚拟的onNodeVisited函数。然后,派生类可以实现它们自己版本的onNodeVisited,而不必担心在基类中定义的树遍历的细节。
不使用纯虚拟函数并将树功能和特定于应用程序的功能保留在一个类(虚拟onNodeVisited)中有意义吗?或者,我是否应该将树类抽象,并为特定于应用程序的部分实现一个子类。
发布于 2011-04-20 11:05:20
我不会现在就决定的。之后,您可以为Tree创建一个抽象基类,并将代码移动到那里,如果这有意义的话。
继承这类东西的另一个选择是调用一个指向函数或函数器的指针类型。它更容易重用,因为您不必为每个新的情况都创建新的类。
发布于 2011-04-20 11:03:41
显然,这必须根据具体情况来决定,对于树的例子来说,一个关键的决定/问题似乎是,树的所有用户是否都需要/想要实现onNodeVisited函数。如果树同样可以与接口的其他部分一起使用(例如,它支持get_next_child()-style迭代,或某种“路径”查找),那么对于从不打算访问每个节点的人来说,树听起来可能很有用,因此也不想实现onNodeVisited函数。在这种情况下,onNodeVisited不应该是纯虚拟的,无论是现在还是将来。如果您的设计决策是让树的接口具有非常严格的限制,以至于类在没有访问的情况下是无用的,那么您可能会坚持让人们通过使onNodeVisited纯虚拟来实现它。
发布于 2011-04-20 12:06:37
我可以看到两个让我印象深刻的选择。
如果您只是在考虑最终可能会变得有用的东西,那么我会选择YAGNI路线,并完全忽略它,直到或除非您发现真正需要它。
如果你非常确定你真的需要它,只是还没有足够的其他代码来使用它,那么就值得在这个类中设计它,即使你还没有使用它。
不过,对于纯虚拟函数来说,这听起来并不是一个好的情况。纯虚函数意味着1)包含纯虚函数的类不能被直接实例化--它只能用作基类,2)为了能够创建派生类的对象,它们必须提供纯虚函数的实现。
换句话说,一个纯粹的虚拟函数将表明几乎与您的情况相反。在类的对象可以存在之前,必须重写纯虚函数。您(至多)正在构建一个“钩子”,以使特定的未来扩展变得容易。
我将重复上面的建议:除非你非常确定这将被使用,否则最好的可能是只设计你真正需要的东西,并把它留在那里。如果你决定你需要(或者真的,真的想)包含它,我会试着让它尽可能的松散耦合。最明显的方法就是使用访问者模式。这定义了一个单独的访问者类,用于处理访问和处理节点。树节点类将包含一个额外的函数(传统名称为accept),该函数接受一个参数:指向访问者对象的指针(或引用)。访问者类是一个典型的抽象基类,带有一个成员函数(传统上称为visit),用于在节点上进行处理。这通常是一个抽象基类。
然后,当您决定真正想要在每个节点上执行什么处理时,可以从visitor派生一个新类,该类将覆盖visit来执行逐个节点的处理。这棵树根本不需要修改。这是众所周知的模式(甚至在C++程序员中也是如此,他们通常对模式不那么“友好”),所以假设您使用普通的名称,大多数人都会很快识别出它。它还有助于代码本身通常非常简单-- accept通常看起来像这样:
struct tree_node;
class visitor {
virtual void visit(tree_node *node) = 0;
};
struct tree_node {
void accept(visitor *v) { v->visit(this); }
// ...
};然后,为了添加处理,您可以从访问者派生,并覆盖visit来执行所需的处理。树方面的东西根本不需要修改。这还具有一个精确性,即可以编写单个访问者类来访问多种类型的节点(即使这些节点不是通过继承相关的)。
https://stackoverflow.com/questions/5725015
复制相似问题