緣起,看到一篇文章(懶得超鏈接),重新研究《C++沉思錄》的那一個課堂練習(xí),綜合利用好幾種設(shè)計模式,并且很好地遵守結(jié)合的面向?qū)ο蟮脑瓌t,嗯,不管怎么樣,還是表揚(yáng)這位同學(xué)的面向?qū)ο笈c設(shè)計模式的功力,確實不容易。只是,在下從模式堆里爬出來之后,已經(jīng)對模式大倒胃口,看到這么小的練習(xí),居然要用上這么多模式,一時技癢,有必要這樣嗎?對我來說,代碼中每引入一種模式,就表示要添加各種接口、虛函數(shù),然后,免不了就要用到繼承,這些,我非常深痛惡疾,因為,這都意味著間接層越來越厚了,偏離了問題的本質(zhì)。
先看看沉思錄對于這道題的描述,嗯,這個,還是請各位自己去看看書是怎么寫的,作者提供了兩種方案,佩服作者和譯者的文筆,兩種方案解釋得相當(dāng)細(xì)膩。下面,嘗試另一種解決方式。先秀一下代碼的運(yùn)行效果。
.PNG)
對于這個課堂練習(xí),其中,給圖片加邊框,最適合于用裝飾模式來解決。但是,兩幅圖片的橫接、豎接,怕是就不那么容易了。但是,首先,要對問題重新描述一下,現(xiàn)實中,一張圖片,可以給予添加一層又一層的片框,也可以和其他的圖片組合在一塊,或橫,或豎,……,但是,圖片,始終只有一張,對它處理之后,它就一直是那個樣子了,不可能同時看到它的兩個樣子。如果,拿這張圖片去進(jìn)行復(fù)制,那又自是另當(dāng)別論,但那已經(jīng)是另外一張新圖片了。
以下的設(shè)計用上了消息,如果有現(xiàn)成消息框架給予支持,實現(xiàn)起來就會很簡單。但是,即使沒有消息框架的支持,利用消息發(fā)送來解決這個問題,也是相當(dāng)小兒科的事情。為了突出重點,忽略了各種異常處理,沒有優(yōu)化,也不管什么編程風(fēng)格,純粹直奔主題。解決這個例子的最重要一點,就在于打印圖片時,要一行一行地從頂?shù)降醉槾未蛴∠聛怼R虼耍榷x這個圖片可以發(fā)送的消息,它必須處理兩條消息,圖片的高度(即行數(shù)),和圖片每一行的數(shù)據(jù)。即如下所示

enum
{PM_HEIGHT, PM_ROW};

struct PictureMsg


{
typedef int (*ProcPictureMsg)(void* pThis, int nMessage, void* param1, void* param2);
public:
void* pThis;
ProcPictureMsg proc;

int GetHeight()

{
return (*proc)(pThis, PM_HEIGHT, NULL, NULL);
}

int GetRow(int nRow, string& sRow)

{
return (*proc)(pThis, PM_ROW, (void*)nRow, (void*)&sRow);
}
};
ostream& operator << (ostream& out, PictureMsg& msg)


{
int nHeight = msg.GetHeight();
string sRow;
for (int nRow = 0; nRow<nHeight; nRow++)

{
sRow.clear();
msg.GetRow(nRow, sRow);
out << sRow;
out << endl;
}
return out;
}

其中的回調(diào)函數(shù)用以發(fā)送消息,本來回調(diào)函數(shù)搞成成員函數(shù)會更加好,但由于c++的成員函數(shù)是一個大坑,而我又不想引入function或者其他delegate等龐然大物。為了讓客戶更加方便地發(fā)送消息,特意編寫了兩個輔助函數(shù)。
但是,要發(fā)送消息給對象時,首先要求這個對象必須已經(jīng)活著了,也即是要創(chuàng)建一個能接收消息的對象。由于沒有消息框架的支持,同時,也為了使用上的方便,我只好編寫一個具體的類繼承于圖片消息結(jié)構(gòu),并提供了初步的消息處理,嗯,用到了繼承,很難受。
好了,接下來就可以全心全意地對消息進(jìn)行特別地處理了。要求,對圖片的各種裝飾操作,不能影響到圖片原有的功能,也不要求改變用戶的代碼。或者說,圖片本身并不知道自己已經(jīng)被外界做了各種各樣的處理,它還是按照原來的樣子處理各種消息。下面,只是實現(xiàn)了兩種裝飾操作,加邊框與橫接,至于豎接,實在很容易實現(xiàn)的。此外,由于在實現(xiàn)裝飾操作時,需要用到圖片的寬度,因此,圖片消息又添加了一條新的消息,用以處理圖片的寬度。
由于是玩具代碼,在下也沒有做太大的優(yōu)化,有幾行代碼顯得特別難看,特別是橫接的裝飾方法,強(qiáng)制指定了左邊的圖片為操作后的結(jié)果,還有各個裝飾類的析構(gòu)函數(shù),可以撤消對圖片的裝飾,以及添加新的消息,以返回圖片的拷貝,或返回當(dāng)前正在進(jìn)行的裝飾操作,或者裝飾類的動態(tài)創(chuàng)建等等。總之,可優(yōu)化升級的方法多種多樣,而且都不會影響代碼的結(jié)構(gòu)。以下,是一陀代碼,從第一版上稍加優(yōu)化的,不讀也罷。


enum
{ PM_WIDTH, PM_HEIGHT, PM_ROW};

struct PictureMsg


{
typedef int (*ProcPictureMsg)(void* pThis, int nMessage, void* param1, void* param2);
public:
void* pThis;
ProcPictureMsg proc;

int GetWidth()

{
return (*proc)(pThis, PM_WIDTH, NULL, NULL);
}

int GetHeight()

{
return (*proc)(pThis, PM_HEIGHT, NULL, NULL);
}

int GetRow(int nRow, string& sRow)

{
return (*proc)(pThis, PM_ROW, (void*)nRow, (void*)&sRow);
}
};

ostream& operator << (ostream& out, PictureMsg& msg)


{
int nHeight = msg.GetHeight();
string sRow;
for (int nRow = 0; nRow<nHeight; nRow++)

{
sRow.clear();
msg.GetRow(nRow, sRow);
out << sRow;
out << endl;
}
return out;
}

class CPicture : public PictureMsg


{
public:
CPicture(const char* pDatas[], int nCount)

{
m_pDatas = pDatas;
m_nCount = nCount;
m_nWidth = 0;
for (int i=0; i<m_nCount; i++)

{
int nLen = strlen(m_pDatas[i]);
if (m_nWidth < nLen)
m_nWidth = nLen;
}
pThis = this;
proc = HandleMsg;
}

private:
const char** m_pDatas;
int m_nCount;
int m_nWidth;

static int HandleMsg(void* pThis, int nMessage, void* param1, void* param2);
};

int CPicture::HandleMsg(void* pThis, int nMessage, void* param1, void* param2)


{
CPicture* pSelf = (CPicture*)pThis;
switch (nMessage)

{
case PM_WIDTH:
return pSelf->m_nWidth;

case PM_HEIGHT:
return pSelf->m_nCount;
break;

case PM_ROW:
int nRow = (int)param1;
string& sRow = *(string*)param2;
if (nRow >= pSelf->m_nCount)
break;
int i=0;
for (; pSelf->m_pDatas[nRow][i] != 0; i++)
sRow.push_back(pSelf->m_pDatas[nRow][i]);
for (; i<pSelf->m_nWidth; i++)
sRow.push_back(' ');
}
return 0;
}


class CFrameDecorater


{
public:
CFrameDecorater(PictureMsg& imp)

{
m_PrevImp = imp;
imp.pThis = this;
imp.proc = HandleMsg;
}

private:
PictureMsg m_PrevImp;
static int HandleMsg(void* pThis, int nMessage, void* param1, void* param2);
};

int CFrameDecorater::HandleMsg(void* pThis, int nMessage, void* param1, void* param2)


{
CFrameDecorater* pSelf = (CFrameDecorater*)pThis;
PictureMsg& prevImp = pSelf->m_PrevImp;
switch (nMessage)

{
case PM_WIDTH:
return prevImp.GetWidth()+2;

case PM_HEIGHT:
return prevImp.GetHeight()+2;

case PM_ROW:
int nRow = (int)param1;
string& sRow = *(string*)param2;
bool bMyRow = nRow == 0 || nRow>prevImp.GetHeight();
if (nRow >= prevImp.GetWidth()+2)
break;
if (nRow == 0 || nRow>prevImp.GetHeight())

{
sRow.push_back('+');
for (int i=0; i<prevImp.GetWidth(); i++)
sRow.push_back('-');
sRow.push_back('+');
}
else

{
sRow.push_back('|');
prevImp.GetRow(nRow-1, sRow);
sRow.push_back('|');
}
}
return 0;
}


class CHorseDecorater


{
public:
CHorseDecorater(PictureMsg& impLeft, PictureMsg& impRight)

{
m_Left = impLeft;
m_Right = impRight;
impLeft.pThis = this;
impLeft.proc = HandleMsg;
}

private:
PictureMsg m_Left;
PictureMsg m_Right;

static int HandleMsg(void* pThis, int nMessage, void* param1, void* param2);
static void PrintRow(PictureMsg& pict, int nRow, string& sRow)

{
if (nRow < pict.GetHeight())
pict.GetRow(nRow, sRow);
else

{
for (int i=0; i<pict.GetWidth(); i++)
sRow.push_back(' ');
}
}
};

int CHorseDecorater::HandleMsg(void* pThis, int nMessage, void* param1, void* param2)


{
CHorseDecorater* pSelf = (CHorseDecorater*)pThis;
PictureMsg& pictLeft = pSelf->m_Left;
PictureMsg& pictRight = pSelf->m_Right;
switch (nMessage)

{
case PM_WIDTH:
return pictLeft.GetWidth()+pictRight.GetWidth();;

case PM_HEIGHT:
return max(pictLeft.GetHeight(), pictRight.GetHeight());

case PM_ROW:
int nRow = (int)param1;
string& sRow = *(string*)param2;
PrintRow(pictLeft, nRow, sRow);
PrintRow(pictRight, nRow, sRow);
}
return 0;
}

int main()


{

const char* init1[] =
{"Paris", "in the", "Spring", "HaHa"};
CPicture pict1(init1, 3);
//cout << pict1;
CFrameDecorater framer1(pict1);
//cout << pict1;
CFrameDecorater framer2(pict1);
//cout << pict1;
CPicture pict2(init1, 4);
CHorseDecorater hors(pict1, pict2);
//cout << pict1;
CFrameDecorater framerHorse(pict1);
//cout << pict1;
CHorseDecorater hors2( pict2, pict1);
//cout << pict2;
CFrameDecorater framer3(pict2);
CHorseDecorater hors3( pict1, pict2);
cout << pict1;
return 0;
}
