While we have decided that out first version of Phish will run on 32-bit Windows, we want our design to maintain a level of abstraction, so that the task of moving Phish to another platform is as straightforward as possible. To this end, we abstract the display from the specific platform-dependent implementation and create PADisplay
as an abstract base class. The PADisplay
class provides an abstract representation of the display, and will be overridden by the framework-specific display class. The PADisplay
class is shown below:
class PADisplay : public RCBody
{
public:
int Height() const {return dHeight;};
int Width() const {return dWidth;};
PADisplay();
virtual ~PADisplay();
virtual PADisplay *NewInstance() const = 0;
virtual void DrawTo ( const PADevice &Device ) = 0;
virtual void Draw
(const PLocation &Loc, const TImagePtr &Icon )=0;
virtual void Draw (int x, int y, const TImagePtr &Bitmap ) = 0;
virtual void Erase (const PLocation &Loc )=0;
virtual void Resize (int width, int height )=0;
virtual TImagePtr CreateBitmap
(const string &Name, RGBColor Color )=0;
protected:
RGBColor dBackColor;
int dHeight;
int dWidth;
typedef RCHandle<PAImage> TImagePtr;
};
In the MFC application, this class is made concrete by CDisplay
. The concrete implementation of CreateBitmap()
in CDisplay
is framework- and operating-system dependent, as you would expect:
TImagePtr CDisplay::CreateBitmap (const string &Name, RGBColor ForeColor )
{
if (!dMemDC) return TImagePtr();
HWND hDesktop = GetDesktopWindow();
CWnd Desktop;
Desktop.Attach (hDesktop);
CWindowDC DummyDC (&Desktop);
Desktop.Detach();
static int LastPos = 0;
CBitmap bwBitmap;
bwBitmap.LoadBitmap (Name.c_str());
CDC bwMemDC;
bwMemDC.CreateCompatibleDC (&dMemDC);
bwMemDC.SelectObject (&bwBitmap);
BITMAP TemporarySolution;
bwBitmap.GetObject
(sizeof(TemporarySolution), &TemporarySolution);
const int SrcHeight = TemporarySolution.bmHeight;
const int SrcWidth = TemporarySolution.bmWidth;
DummyDC.BitBlt (LastPos, 0, 11, 11, &bwMemDC, 0, 0, SRCCOPY);
LastPos+=10;
CDC colMemDC;
colMemDC.CreateCompatibleDC(&dMemDC);
CBitmap *pcolBitmap = new CBitmap;
CBitmap &colBitmap = *pcolBitmap;
colBitmap.CreateCompatibleBitmap(&DummyDC, SrcHeight, SrcWidth);
colBitmap.GetObject
(sizeof(TemporarySolution),&TemporarySolution);
bwBitmap.GetObject
(sizeof(TemporarySolution),&TemporarySolution);
colMemDC.SelectObject (&colBitmap);
colMemDC.SetBkColor (ConvertColor(dBackColor));
colMemDC.SetTextColor (ConvertColor(ForeColor));
colMemDC.BitBlt
(0, 0, SrcWidth, SrcHeight, &bwMemDC, 0, 0, SRCCOPY);
DummyDC.BitBlt(LastPos,0,10,10,&colMemDC, 0,0,SRCCOPY);
LastPos+=10;
colMemDC.SelectObject ((CBitmap*)0);
return TImagePtr (new CImage(pcolBitmap));
};
All these ugly details are encapsulated by the platform-specific classes and hidden to clients of PADisplay
, as they should be. The abstract display class knows that it has a height, width and background color but not how these are manipulated. The concrete display class manages device contexts, bit maps, pixels and so forth.
The PADisplay
works hand in glove with the abstract base class PADevice
and the concrete CDevice
. Again, the specifics of the operating system devices are hidden behind the abstraction layer.
The CPhishDlg
dialog class has a member variable, dPhishWnd
, which is an instance of the class CPhishWnd
. This represents the window in which we'll display the Phish tank. CPhishWnd
is a CStatic
window and is MFC specific, as is CPhishDlg
.
CPhishWnd
in turn contains a data member, dTank
— an instance of the PPhishTank
class. A PPhishTank
object contains a TDisplayPtr
, which is a pointer to a PADisplay
:
typedef RCHandle<PADisplay> TDisplayPtr;
This allows us to polymorphically create concrete display objects (such as CDisplay
), which can be platform-specific.
class PPhishTank : public RCBody
{
protected:
TDisplayPtr pDisplay;
The constructor of the CPhishWnd
creates the PPhishTank
passing in a new CDisplay
(derived from PADisplay
):
CPhishWnd::CPhishWnd() : dTank(new CDisplay)
{
}
When the PPhishTank
or the PSimulator
object need to be drawn, the work is delegated to PPhishTank
's pDisplay
member, thus creating platform-specific implementations of all the drawing methods.