views:

210

answers:

3

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!

A: 

To hide the implementation details, I would suggest using the pImpl idiom.

Visage
+3  A: 

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.

Space_C0wb0y
1 step further for hiding would be to provide 1 function and keep the complete factory-class internally.
stefaanv
exactly, so I guess it should be a singleton used in the factory method implementation?
davka
Not necessarily: you can also have a class instantiating the factory using the `pimpl` idiom, which allow to have multiple factories... if that makes sense to you. However in most cases those factories are indeed intended to be unique, and thus implemented as `Singleton`.
Matthieu M.
A: 

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);
}
iain
thanks a lot for a detailed answer! I have a following concern, though:I expect (need to investigate it) that I'll want to have one concrete object of each kind and to share it with all clients. This is because some of the object will have quite a large cache, and constructing them will require reading a configuration. If I clone the object for clients I'll need to implement some cache-sharing mechanism. An option could be to keep a static map of objects by name (somewhere), and either to construct all of them on startup or to check upon request whether it was already constructed?
davka
To share the concrete objects you should probably return a share_ptr. There are advantages to both pre-creating the objects and on creating them demand. Pre-created might slowdown startup and use up more memory than required but will help with threading issues in getEcryption(). Ondemand has faster startup but the calls to getEcryption can have unpredictable timing, also it is more complicated to implement. If you don't mind the complicated implementation and want memory efficiency you can store weak_ptr's in the cache.
iain