I was going through the "Modern C++ Design" book, I saw the below code explained in "8.1 The Need for Object Factories", and I have some doubts.
If No, then "CreateDocument()" should take an ID so that it can decide exactly what to create among a of objects. But this also means, each time a derivedDocument is created, he also should find the correct documentManager and update the CreateDocument() method. But we have to decide to have or not to have the a Factory of documentManagers as there can be few or many of them.
My main doubt is whether this is a solution at all or if I am missing the point. According to the book, CreateDocument()
is the GoF book's factory method. "CreateDocument()" creating a new derivedDocument based on an ID and lots of conditions at least makes sense. But lots of derivedDocumentManager does not make sense.
DocumentManager
from the book,
class DocumentManager
{
...
public:
Document* NewDocument();
private:
virtual Document* CreateDocument() = 0;
std::list<Document*> listOfDocs_;
};
Document* DocumentManager::NewDocument()
{
Document* pDoc = CreateDocument();
listOfDocs_.push_back(pDoc);
...
return pDoc;
}
Client code:
Document* GraphicDocumentManager::CreateDocument()
{
return new GraphicDocument;
}
UPDATE: your question's been heavily edited to give it a different focus. A few more points:
Have a read about the Factory Method Pattern and hopefully you'll realise that's what you have. It lets you do things like:
std::string compress(DocumentManager& d, const std::string& uncompressed)
{
std::unique_ptr<Document> doc = std::make_unique(d.NewDcoument());
doc = uncompressed; // interpret to form document of whatever type
return zlib::compress(d.data(), d.size()); // compress as binary blob
}
Here, the one compress()
function can be called with some raw input and attempt to first create a document of the right caller-nominated type then stuff some data into it and compress it....
The factory aspect is the ability of compress to create an object without knowing the concrete type involved, as - being untemplated and lacking any switching - it can't otherwise choose between many constructors.
Answer based on original questions...
Are we not moving the problem of object creation from
Document
toDocumentManager
class. I mean we have to create [concrete]DocumentManager
?
That's the point isn't it - to have extra record-keeping (in this case the list<Document*>
) around the creation of specific types of Document
s.
If we let client code create Document
s directly, having such a list would depend on the client updating the list every time they create an object (or require invasive changes to the Document
constructors). If we wanted to say add timestamps for when the object creations we'd have to modify every place in the client code that creates any type of Document
.
If we didn't have a DocumentManager
base class, we'd have to put similar logic and data members into any number of GraphicDocumentManager
, TextDocumentManager
, AudioDocumentManager
etc., and couldn't handle those managers polymorphically (e.g. creating a vector<DocumentManager*>
, writing functions ala void f(DocumentManager&);
.
How to keep track of all the derived classes of
DocumentManager
, created by many independent clients?
There's nothing in the design intended to do that, and C++ doesn't have introspective facilities that would let you enumerate the derived types - neither at compile-time nor when the program starts running. But, if you're prepared to wait for NewDocument
to be called you could record the addresses of concrete DocumentManager
-derived objects and/or access their RTTI information (which would e.g. let you count the distinct types of documents that had been created, or try displaying the implementation-defined (and possibly empty) name()
field of the dynamic types....
Should not the listOfDocs_ be static and DocumentManager be a singleton.
Probably not. The program might want to do something like keep a DocumentManager
object per TCP client, per filesystem, per user etc. - so why limit it unnaturally? It's easy and better to make a more flexible DocumentManager
type, then let client code apply a singleton wrapper (e.g. function returning a static
instance) if that's actually useful for them. That also makes it easier to test: you can freely create DocumentManager
s, run a test, let destructors run, create another etc....
My main doubt is, is that a solution at all or I am missing some point.
Seems you're missing something "bit picture", but without the book to hand or more general question/statements-of-understand from you it's hard to know what else it might be.
According to the book "CreateDocument()" is the GoF book's factory method. But I am not able to understand the above points.
Yes - that's a factory method, as it creates any of many different dynamic types of objects, but returns a pointer-to-base-class by which they can be handled polymorphically.