这是使用 PIMPL 模式的好地方吗?
我正在开发一个为某些服务定义客户端接口的库。在幕后,我必须验证用户提供的数据,然后使用另一个库中的 Connection 类将其传递给“引擎”进程(注意:我们库的用户不知道 Connection 类)。我的一位同事建议使用 PIMPL:
class Client {
public:
Client();
void sendStuff(const Stuff &stuff) {_pimpl->sendStuff(stuff);}
Stuff getStuff(const StuffId &id) {return _pimpl->getStuff(id);}
private:
ClientImpl *_pimpl;
}
class ClientImpl { // not exported
public:
void sendStuff(const Stuff &stuff);
Stuff getStuff(const StuffId &id);
private:
Connection _connection;
}
但是,我发现测试非常困难 - 即使我将测试链接到 Connection 的某些模拟实现,我也无法轻松访问它来设置和验证期望。我是否遗漏了一些东西,或者更干净且可测试的解决方案是使用接口+工厂:
class ClientInterface {
public:
void sendStuff(const Stuff &stuff) = 0;
Stuff getStuff(const StuffId &id) = 0;
}
class ClientImplementation : public ClientInterface { // not exported
public:
ClientImplementation(Connection *connection);
// +implementation of ClientInterface
}
class ClientFactory {
static ClientInterface *create();
}
在这种情况下有什么理由使用 PIMPL 吗?
I'm working on a library that defines a client interface for some service. Under the hood I have to validate the data provided by users and then pass it to "engine" process using Connection class from another library (note: the Connection class isn't known to the users of our library). One of my colleagues proposed using PIMPL:
class Client {
public:
Client();
void sendStuff(const Stuff &stuff) {_pimpl->sendStuff(stuff);}
Stuff getStuff(const StuffId &id) {return _pimpl->getStuff(id);}
private:
ClientImpl *_pimpl;
}
class ClientImpl { // not exported
public:
void sendStuff(const Stuff &stuff);
Stuff getStuff(const StuffId &id);
private:
Connection _connection;
}
However, I find it very hard to test - even if I link my tests to some mocked implementation of Connection, I don't have an easy access to it to set and validate expectations. Am I missing something, or the much cleaner and testable solution is using interface + factory:
class ClientInterface {
public:
void sendStuff(const Stuff &stuff) = 0;
Stuff getStuff(const StuffId &id) = 0;
}
class ClientImplementation : public ClientInterface { // not exported
public:
ClientImplementation(Connection *connection);
// +implementation of ClientInterface
}
class ClientFactory {
static ClientInterface *create();
}
Are there any reasons to go with PIMPL in this situation?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
据我所知,使用 Pimpl 习惯用法的通常原因是减少对类实现的编译/链接时间依赖性(通过从公共头文件中完全删除实现细节)。另一个原因可能是使类能够动态更改其行为(也称为状态模式)。
这里第二个似乎不是这样,第一个也可以通过继承+工厂来实现。然而,正如您所指出的,后一个解决方案更容易进行单元测试,所以我更喜欢这个。
AFAIK the usual reason to use the Pimpl idiom is to reduce compile/link time dependencies to the implementation of the class (by removing the implementation details from the public header file altogether). Another reason may be to enable the class to change its behaviour dynamically (aka the State pattern).
The second does not seem to be the case here, and the first can be achieved with inheritance + a factory as well. However, as you noted, the latter solution is much easier to unit test, so I would prefer this.
GoTW15
GoTW28
来自赫伯·萨特。很好的指导,可以帮助您入门。
GoTW15
GoTW28
From Herb Sutter. Good pointers to get you started.
是的,这是使用 Pimpl 模式的好地方,是的,按原样测试会很困难。
问题是这两个概念相互对立:
但是,这并不意味着您应该牺牲一个来换取另一个。这仅仅意味着您应该调整您的代码。
现在,如果
Connection
是用相同的习惯用法实现的呢?并通过工厂交付:
这样,您可以在测试客户端时访问连接测试实现的具体细节,而无需向客户端公开实现或破坏 ABI。
Yes this is a good place to use the Pimpl pattern, and yes it will be difficult to test as is.
The problem is that the two concepts oppose one another:
However, it does not mean that you should sacrifice one for another. It merely means that you should adapt your code.
Now, what if
Connection
was implemented with the same idiom ?And delivered through a Factory:
This way, you can access the nitty gritty details of your test implement of connection while testing client without exposing the implementation to the client or breaking the ABI.