Today a lession on class factories, also called "virtual constructors".
Now what is that, do I need one?
To answer that question I'll have to describe which problem it solves.
On my samples page I have tree versions of a beetle program, they all to the same thing, but on different platforms. If we were to do it in a Object Oriented way, we could create a number of classes which encapsulates the drawing, then we could have a common logic, and do the tree versions by simply using a class specific for the platform on which we want it to run.
We startup by defining a base class with only pure virtual functions, some calls this a virtual base class:
class DrawBaseClass { public: virtual ~DrawBaseClass() {} void ClearScreen() = 0; void GotoXY(int x, int y) = 0; void PutCh(char ch) = 0; };A pure virtual function is a function which all derived classes must implement, but the base does not.
You can't create an instance of a class with pure virtual funcions. Pure virtual functions are never called. All classes with virtual functions should have a virtual destructor, the derived classes destructors might not be called otherwice. A virtual destructor can be made pure, but must allways be implemented.
Then we can derive a class based on the base class for all the platforms we support, say:
class ConsoleDrawClass : public DrawBaseClass { public: virtual ~ConsoleDrawClass() {} void ClearScreen(); void GotoXY(int x, int y); void PutCh(char ch); private: HANDLE ConsoleHandle; };Then the user can use the derived class:
DrawBaseClass *Draw = new ConsoleDrawClass;And access the console functions thru this pointer:
Draw->GotoXY(3, 12);If the user want to do a Windows version of the beetle he just do:
DrawBaseClass *Draw = new WindowsDrawClass;And all is set, but...
The problem is that the derived classes will contain information which is not a part of the interface but is implementation details. In the ConsoleDrawClass case it is the ConsoleHandle, the user should not have to know what a HANDLE is, so we better hide this information, by removing it from header.
But the user needs to know the ConsoleDrawClass class to create one, right?
Normally yes, but this is were the class factory comes handy.
If we create a function:
DrawBaseClass *CreateConsoleDraw() { return new ConsoleDrawClass; }The user can call this function to get a ConsoleDrawClass, and he does not have to know the ConsoleDrawClass class; CreateConsoleDraw is a class factory.
Now we can split the program i manageable parts:
- draw.h with DrawBaseClass, and a prototype for CreateConsoleDraw and other class factories for classes derived from DrawBaseClass.
- console.cpp which defines ConsoleDrawClass class, implements its functions, and CreateConsoleDraw
- main.cpp, the user implementation, which includes draw.h, and calls CreateConsoleDraw to create a interface, and uses the returned pointer to do its drawing.