程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> C++沉思錄課堂練習另解

C++沉思錄課堂練習另解

編輯:關於C
緣起,看到一篇文章(懶得超鏈接),重新研究《C++沉思錄》的那一個課堂練習,綜合利用好幾種設計模式,並且很好地遵守結合的面向對象的原則,嗯,不管怎麼樣,還是表揚這位同學的面向對象與設計模式的功力,確實不容易。只是,在下從模式堆裡爬出來之後,已經對模式大倒胃口,看到這麼小的練習,居然要用上這麼多模式,一時技癢,有必要這樣嗎?對我來說,代碼中每引入一種模式,就表示要添加各種接口、虛函數,然後,免不了就要用到繼承,這些,我非常深痛惡疾,因為,這都意味著間接層越來越厚了,偏離了問題的本質。
          先看看沉思錄對於這道題的描述,嗯,這個,還是請各位自己去看看書是怎麼寫的,作者提供了兩種方案,佩服作者和譯者的文筆,兩種方案解釋得相當細膩。下面,嘗試另一種解決方式。先秀一下代碼的運行效果。
\

          對於這個課堂練習,其中,給圖片加邊框,最適合於用裝飾模式來解決。但是,兩幅圖片的橫接、豎接,怕是就不那麼容易了。但是,首先,要對問題重新描述一下,現實中,一張圖片,可以給予添加一層又一層的片框,也可以和其他的圖片組合在一塊,或橫,或豎,……,但是,圖片,始終只有一張,對它處理之後,它就一直是那個樣子了,不可能同時看到它的兩個樣子。如果,拿這張圖片去進行復制,那又自是另當別論,但那已經是另外一張新圖片了。
          以下的設計用上了消息,如果有現成消息框架給予支持,實現起來就會很簡單。但是,即使沒有消息框架的支持,利用消息發送來解決這個問題,也是相當小兒科的事情。為了突出重點,忽略了各種異常處理,沒有優化,也不管什麼編程風格,純粹直奔主題。解決這個例子的最重要一點,就在於打印圖片時,要一行一行地從頂到底順次打印下來。因此,先定義這個圖片可以發送的消息,它必須處理兩條消息,圖片的高度(即行數),和圖片每一行的數據。即如下所示
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;
} www.2cto.com
 
          其中的回調函數用以發送消息,本來回調函數搞成成員函數會更加好,但由於c++的成員函數是一個大坑,而我又不想引入function或者其他delegate等龐然大物。為了讓客戶更加方便地發送消息,特意編寫了兩個輔助函數。
          但是,要發送消息給對象時,首先要求這個對象必須已經活著了,也即是要創建一個能接收消息的對象。由於沒有消息框架的支持,同時,也為了使用上的方便,我只好編寫一個具體的類繼承於圖片消息結構,並提供了初步的消息處理,嗯,用到了繼承,很難受。
          好了,接下來就可以全心全意地對消息進行特別地處理了。要求,對圖片的各種裝飾操作,不能影響到圖片原有的功能,也不要求改變用戶的代碼。或者說,圖片本身並不知道自己已經被外界做了各種各樣的處理,它還是按照原來的樣子處理各種消息。下面,只是實現了兩種裝飾操作,加邊框與橫接,至於豎接,實在很容易實現的。此外,由於在實現裝飾操作時,需要用到圖片的寬度,因此,圖片消息又添加了一條新的消息,用以處理圖片的寬度。
由於是玩具代碼,在下也沒有做太大的優化,有幾行代碼顯得特別難看,特別是橫接的裝飾方法,強制指定了左邊的圖片為操作後的結果,還有各個裝飾類的析構函數,可以撤消對圖片的裝飾,以及添加新的消息,以返回圖片的拷貝,或返回當前正在進行的裝飾操作,或者裝飾類的動態創建等等。總之,可優化升級的方法多種多樣,而且都不會影響代碼的結構。以下,是一陀代碼,從第一版上稍加優化的,不讀也罷。
 
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;
}

作者 huaxiazhihuo
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved