building a factory with object repository in C++?
I want to create a factory for creation of objects implementing an abstract interface, which would return a reference to the object that is kept internally, and objects are not replicated. The idea is pretty much the same as in the log4cxx/log4j Logger class design. I would also like to hide as much details from the client as possible, i.e. that viewing the exposed .h file would not reveal implementation details like private members etc. Example:
EncryptorRef = Encryptor::getEncryptor("AES");
I wonder whether there accepted published guidelines/sample code for such design, as I wouldn't like to reinvent the wheel, and the task is pretty common. I thought of using static Factory Method开发者_如何转开发, Singleton repository inside, and smart pointer/reference to the concrete object as returned type. questions:
- is there a sample simple code for such design? (the code of log4cxx is too complex to be used as a skeleton)
- how do I hide the repository from the client completely, assuming he only sees the pure abstract
Encryptor
class defined encryptor.h? - would you suggest using smart reference or pointer as a return type? is there a standard implementation for smart reference?
- any other suggestions will be appreciated
thanks a lot!
Using a smart pointer as a return value is only usefull if there is any cleanup needed when the client doesn't need the reference to the object anymore (e.g. releasing some lock or other resource, or decrementing some reference count). If no such thing is necessary, I suggest returning a simple reference. This way, the client knows he doesn't have to manage the life-cycle of the object or anything like that. A standard implementation for smart references would be Boost.SmartPtr. As for hiding the implementation, just put the interfaces you want to expose into pure abstract base classes and let the client obtain instances via factories. All he needs then are the headers with the abstract base classes, the headers with the factory declarations and the binary to link to.
To hide the implementation details, I would suggest using the pImpl idiom.
To hide the implementation details make Encryption class pure virtual, with no data. This keeps the primary header file simple and without implementation details. If you want to use inheritance for code reuse then use a intermediate class like BaseEncryptionImpl (this would be in a private/implementation header file).
Only the source file that implements the static factory method getEncryptor
has to include the Encryption implementations.
This factory method should return an std::auto_ptr
as opposed to a raw pointer to be exception safe. The much maligned auto_ptr
is designed for returning pointer from functions. Also it reduces the external dependencies of your header to a standard library as opposed to boost. Users of your class can use boost::smart_prt
or boost::scoped_ptr
as appropriate to their needs, both have auto_ptr
constructors.
Initially I would keep the getEncryptor
as simple as possible, probably using if else if
etc. to decide which you should create. This is much simpler than implementing a registry of AbstractFactory in a singleton. And most of the time the registry is just moving the problem. How do you initialise the registry? You can use static objects defined with each EncryptionImpl
class, whose constructors register and the destructor unregisters but this can cause problems if the linker decides you don't need these objects and so doesn't include them in your executable or library.
Encryptor.h
class Encryptor {
public:
virtual void encrypt(const Data & in, Data * out) = 0;
virtual ~Encryptor();
static std::auto_ptr<Encryptor> getEncryptor(const char * name);
};
Encryptor.cpp
#include "Encryptor.h"
#include "EncryptorA.h"
#include "EncryptorB.h"
std::auto_ptr<Encryptor> Encryptor::getEncryptor(const char * name)
{
// EncryptorA::NAME is a std::string
if (EncryptorA::NAME == name) {
return std::auto_ptr<Encryptor>(new EncryptorA);
}
else if (EncryptorB::NAME == name) {
return std::auto_ptr<Encryptor>(new EncryptorB);
}
else {
throw EncryptionNotDefined;
}
}
Client.cpp
void foo()
{
boost::scoped_ptr enc(Encryption::getEncryption("FOO"));
Data in = ...;
Data out = ...;
enc->encrypt(in, &out);
}
精彩评论