堆栈分配RAII对象与DI原理

Aby*_*byx 7 c++ unit-testing dependency-injection raii

在C++中,我经常使用RAII样式的对象使代码更可靠,并在堆栈上分配它们以使代码更高效(并避免使用bad_alloc).

但是在堆栈上创建具体类的对象违反了依赖性反转(DI)原则并且防止模拟该对象.

请考虑以下代码:

struct IInputStream
{
    virtual vector<BYTE> read(size_t n) = 0;
};

class Connection : public IInputStream
{
public:
    Connection(string address);
    virtual vector<BYTE> read(size_t n) override;
};

struct IBar
{
    virtual void process(IInputStream& stream) = 0;
};

void Some::foo(string address, IBar& bar)
{
    onBeforeConnectionCreated();
    {
        Connection conn(address);
        onConnectionCreated();
        bar.process(conn);
    }
    onConnectionClosed();
}
Run Code Online (Sandbox Code Playgroud)

我可以测试IBar::process,但我也想测试Some::foo,而不是创建真正的Connection对象.

当然我可以使用工厂,但它会使代码复杂化并引入堆分配.
另外,我不喜欢添加Connection::open方法,我更喜欢构造完全初始化和功能齐全的对象.

我会为Connection类型设置一个模板参数Some(或者foo如果将其作为自由函数提取),但我不确定它是否正确(模板看起来像许多人的黑魔法,所以我更喜欢使用动态多态)

Jon*_*Jon 7

你现在正在做的是"强制耦合"RAII类和服务提供者类(如果你想要可测试性,它应该真的是一个接口).解决这个问题:

  1. 抽象ConnectionIConnection
  2. 有一个单独的ScopedConnection类,提供RAII

例如:

void Some::foo(string address, IBar& bar)
{
    onBeforeConnectionCreated();
    {
        ScopedConnection conn(this->pFactory->getConnection());
        onConnectionCreated();
        bar.process(conn);
    }
    onConnectionClosed();
}
Run Code Online (Sandbox Code Playgroud)

  • 并且接受`ScopedConnection`不需要被模拟,即使在应该隔离`Some :: foo`的测试中使用真实版本也是"安全的".或者,如果这是不可接受的,请抓住你的牙齿并将其作为模板参数注入,或使用`scoped_ptr`来提供RAII,作为标准类(或第三方,如果你还在使用C++ 03)是可接受的硬件依赖. (2认同)