用虚拟方法替换功能被认为是有害的?

时间:2013-07-09 19:17:05

标签: design-patterns

假设你有一堆相互调用的函数。你想独立地测试它们中的每一个。例如。你应该能够在没有Bar正确甚至实现的情况下测试Foo。一种解决方案是将这些函数转换为虚拟方法(如果您不使用C ++,则只需常规方法)。 E.g。

class K {
 public:
  virtual int Foo();
  virtual int Bar();   // called by Foo

  // No member variables.
};

现在,您可以在测试中执行此操作:

// You could use googlemock instead of this.
class KFoo : public K {
 public:
  virtual int Bar() {
    return 42;
  }
};

// googletest
TEST(K, Foo) {
  std::unique_ptr<K> k(new KFoo);
  assert(k->Foo() == 7);  // Bar gets called, and returns 42.
}

这有点奇怪,但有几个原因:

  1. K没有成员变量;它是“无国籍的”。
  2. 您需要new K才能拨打任何电话。
  3. 这是个好主意吗?这已经有了名字吗?显然,虚拟减慢了速度,但是IIUC,除非你进行大量的呼叫,否则性能影响很小(而且你知道他们对过早优化的看法)。我对这将如何影响可维护性,可测试性等更感兴趣。

1 个答案:

答案 0 :(得分:1)

我在C ++中无法确定,但我有C#(完整OOP)的经验。

我认为在C#中,您提到的设计是抽象类/继承。如果我没弄错的话,你在C ++中的设计将在C#中变成这样:

public class K{
    public virtual int Foo(){
        return Bar();
    }
    public virtual int Bar(){
        // actual code
    }
}

public class KFoo : K{
    public override int Bar(){
        return 42;
    }
}

这种设计是OOP的基础,所以它没有害处。但是,随着更多的继承被添加(K2继承K,K3继承K2等),它将变得更加复杂。作为附加信息,您可以阅读此answer解释我的观点,为什么有时基于继承/类的方法有时不像您想象的那样模块化。

恕我直言,如果你注入“服务”类而不是定义继承,它将是模块化的,它被称为组合而不是继承。

public class KBarService : IKBarService{
    public int Bar(){
        // actual code
    }
}
public class KBarServiceMock : IKBarService{
    public int Bar(){
        return 42;
    }
}
public class K{
    public K(IKBarService service){
        this.service = service;
    }
    IKBarService service;

    public int Foo(){
        return Bar();
    }
    public int Bar(){
        return service.Bar();
    }
}

您可以对Foo()执行相同的操作。使用此设计,您可以在其他类中重用KBarService,即使该类不是派生类形式K.