非虚拟接口(interface)惯用语(NVI)很容易说明:您没有编写public virtual函数,但是编写了public实现函数的private virtual函数,如下所示:

class Object{
    virtual void v_load();
public:
    void load(){ v_load(); }
}

这使您(基类的作者)可以检查并强制执行前置条件和后置条件或应用其他功能,以便派生类的作者不会忘记它们。

现在,当您是派生作者时,您可能想自己编写一个基类-我们称之为Pawn-它扩展了load()的功能,因此必须覆盖v_load()。但是现在您面临一个问题:

当您重写v_load()时,想要从您的类派生的其他客户端将始终覆盖该行为,并且由于它是Pawn::v_load()函数而无法调用private,他们也不能调用Pawn::load(),因为在{ v_load; }中它被定义为Object,当然会导致无限循环。此外,要求他们这样做可能会在他们忘记该 call 时导致错误。如果我希望他们启用它,则必须将v_load()的访问权限指定为protected中的Object,这似乎是一个丑陋的解决方案,因为它会大大削弱Object的封装。

当然,您仍然可以重写v_load()来调用新函数v_pawnLoad(),该新函数随后将被客户端覆盖,但这似乎很容易出错,因为许多客户端可能会重载错误的函数。

因此,如何设计Pawn,使客户端仍然可以覆盖v_load(),同时保持检查前提条件或调用其他功能的能力,并且(如果可能)不启用,更不用说要求ObjectPawn的客户端调用基础了v_load()实现?

最佳答案

  • 如果您打算让人们“扩展”而不是“替换” load的行为,则将您当前在v_load中拥有的代码放入load中,然后最后调用一个空的v_load
  • 或者,如果您想让人们在“替换”或“扩展”之间进行选择,则可以仅使v_load成为protected
  • 如果您只想允许他们替换行为,那么您的代码就可以了。

  • 另外,在所有这三种变体中,如果没有默认行为,则可以将v_load设为纯虚拟,从而将“allow”更改为“force”。

    如果希望将替代项限制为Pawn子类,则将final关键字添加到v_load中的Pawn中,并使用另一个虚拟函数允许Pawn的子项自定义其行为。

    07-24 14:30