Is this a good place to use PIMPL pattern?
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?
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
From Herb Sutter. Good pointers to get you started.
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:
- Pimpl is about hiding dependencies from the client: this reduces compile/link time and is better from an ABI stability point of view.
- Unit Testing is usually about surgical intervention in the dependencies (use of mock ups, for example)
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 ?
class Connection
{
private:
ConnectionImpl* mImpl;
};
And delivered through a Factory:
// Production code:
Client client = factory.GetClient();
// Test code:
MyTestConnectionImpl impl;
Client client = factory.GetClient(impl);
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.
精彩评论