onu*_*lik 4 c++ polymorphism dependencies break
我的A类依赖于B类.这是代码
//declaration
class A
{
public:
A(B *b);
~A();
void m1();
private:
B *ptr_b;
};
//implementation
A::A(B *b)
{
ptr_b = b;
}
A::~A()
{
delete ptr_b;
}
void A::m1()
{
ptr_b->m2();
}
Run Code Online (Sandbox Code Playgroud)
我想用以下解决方案打破这种依赖性(用于单元测试).这是代码
class FakeB : public B
{
public:
FakeB();
~FakeB();
virtual void m2() = 0;
};
class StubB : public FakeB
{
public:
StubB();
~StubB();
void m2();
}
Run Code Online (Sandbox Code Playgroud)
但是当我实例化类A并使用以下代码调用其方法m1()时
A *ptr_a = new A(new StubB);
ptr_a->m1();
Run Code Online (Sandbox Code Playgroud)
方法m1()调用B的方法m2(),因为B的m2()不是虚拟的.B类是来自另一个模块的遗留代码,我不想改变它的代码,但我也不想改变A类的代码.
任何打破这种依赖的解决方案?
首先,由于delete ptr_b;在A new B()的构造函数中没有,因此在类A的析构函数中有一个错误的设计.这意味着每次创建A的实例时,您将B对象的所有权转移到A,留下您的delete使用A的人不知道内部的重复的潜在风险.
其次,如果你想给A一个"存根"(或"模拟"或"虚假")对象而不是"真实B",B并且FakeB需要一个包含B所有方法的公共接口,A需要它作为虚拟方法:
class FakeB : public InterfaceB
Run Code Online (Sandbox Code Playgroud)
和
class B : public InterfaceB
Run Code Online (Sandbox Code Playgroud)
所以A的所有成员函数都可以使用类型的参数InterfaceB *而不是B *.然后注入一个FakeB对象A显然很容易.
不幸的是,这意味着你必须改变B(至少,一点点).如果这不是一个选项,总有可能通过某个类包装B WrapperB
(它与经典的Adapter模式大致相同):
class WrapperB: public InterfaceB
{
B _b;
public:
WrapperB(/* some parameters */) : _b(/* same parameters */){}
// Here you need to implement all methods of
// InterfaceB and delegate them to the original method calls
// of _b. You should give them the same name and signature as
// the corresponding (non-virtual) methods in B.
// For example, if there is a method m2 in B,
// there should be a pure virtual method m2 in InterfaceB, and
// an implementation here like this:
virtual void m2(){ _b.m2(); }
};
Run Code Online (Sandbox Code Playgroud)
WrapperB将只包含非常简单,直接的方法委托代码,您可以省略单元测试.而且你必须使用WrapperB而不是B当你将它与A一起使用时.但你得到的是一个完美的单元可测试class A.
另一个(可能更好)变体是以一种方式构造WrapperB类,在这种方式中,从外部向外部注入B对象的引用:
class WrapperB: public InterfaceB
{
B& _b;
public:
WrapperB(B& b) :_b(b){}
// implement InterfaceB methods as above
virtual void m2(){ _b.m2(); }
}
Run Code Online (Sandbox Code Playgroud)
您可以像这样使用它:
B b;
A a(WrapperB(b));
FakeB fb;
A a_for_test(fb);
Run Code Online (Sandbox Code Playgroud)