23.12 Portable classes

When you develop a general-purpose class like cRandomizer, cVector, cRealBox, cRealPixelConverter, or cMemoryDC, it's a good idea to try and keep the class as portable as possible. By 'portable,' we mean not having many dependencies upon other classes. A class that you develop as a programmer is like a tool that you build to help yourself do things. If you plan to keep programming for a number of years, you don't want to have to redesign your tools any more often than necessary. And, after all, C++ is a language meant to run on all kinds of machines, from Windows to Mac to Linux, so it would be nice to be able to take your tools with you if you happen to migrate.

One way to judge how independent a class is by looking at how many #include lines it has at the top of its definition *.h file and its implementation *.cpp file. Because of the demanding nature of MFC with the AFX application frameworks, we are forced to put the #include <stdafx.h> at the head of every *.cpp file that gets linked into our MFC project. So this one line's worth of linkage to MFC is unavoidable. But it could be unwise to bet the farm on MFC and, for instance, derive something as simple as cVector from the MFC base class CObject.

Let's look at the relationships among the special classes just mentioned. If you flip through the header files, you'll find that cRandomizer, cVector, and cRealPixelConverter use no other classes at all, although they do all use the Real type. But Real is simply a typedef we made in realnumber.h to stand for double, so it's use puts no crimp in portability.

The cMemoryDC class, on the other hand, is implemented in such a Windows-specific fashion that we don't worry about making it very MFC. The cMemoryDC is in fact a child of the CDC. Although the idea of a memory bitmap buffer is something you would want to use on any platform you work on, implementing something like this is so operating-system-specific that it's impossible to accomplish it in a portable manner. Actually, if you use the Swing graphics classes in Java, you don't need to implement double buffering, it happens automatically.

What about cRealBox ? Well, thanks to having a cVector randomVector(cRandomizer &rand) method and a draw(CDC *pDC, const cRealPixelConverter &rtop, BOOL bSolid) method, the cRealBox uses all four of the other classes, not to mention the Real type and MFC. With cRealBox we're moving closer to the code for specific applications.

The Pop Framework classes are so far down into a specific kind of application that we don't worry quite as much about their generality. Even so, there is the possibility that we might someday port these classes to a different platform or even to a different OO language such as Java or C#. So we encapsulate as much of the Windows-specific things as we can. The various cSprite drawing methods use Windows graphics code.

One specifically MFC thing that we do with most of our classes is to make them inherit from the general MFC base class called cObject. Doing this allows us to include the DECLARE_SERIAL macros in the headers and the IMPLEMENT_SERIAL macros in the *.cpp files. The purpose of this declaration is twofold: (a) it makes the class support the MFC model for runtime class information, which involve such constructs as the macro RUNTIME_CLASS(...), the CObject methods GetRuntimeClass and IsKindOf, and the CRuntimeClass method CreateObject ; and (b) it makes it possible to use the CObject::Serialize method to save and load the objects' parameter information with external files.

    Part I: Software Engineering and Computer Games
    Part II: Software Engineering and Computer Games Reference