首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

消息驅動 vs 設計模式:C++ 沉思錄練習題「圖片裝飾與組合」的另類極簡解法

     編輯:關於C

緣起與核心觀點

《C++ 沉思錄》課堂練習要求:圖片支援加邊框、水平/垂直拼接,並能疊加多層裝飾。教科書級答案動用裝飾模式、組合模式、介面繼承、虛函數等重型武器。作者認為:**消息驅動(Message Passing)才是本質解法**,無需繼承、虛函數、任何設計模式,幾十行代碼徹底搞定。

問題本質重述

  • 圖片本質是**字符矩陣**,顯示時**逐行輸出**。
  • 裝飾(邊框)與組合(拼接)本質是**對「取寬、取高、取第 n 行內容」操作的攔截與轉發**。
  • 圖片物件**不變**,裝飾層僅在輸出時動態修飾行內容。

核心抽象:PictureMsg 介面(純 C 風格消息結構體)


enum { PM_WIDTH, PM_HEIGHT, PM_ROW };

typedef int (*ProcMsg)(void* obj, int msg, void* p1, void* p2);

struct PictureMsg {
    void* obj;      // 承載具體實現的 this 指標
    ProcMsg proc;   // 消息處理函數
    // 輔助方法
    int GetWidth()  { return proc(obj, PM_WIDTH, 0, 0); }
    int GetHeight() { return proc(obj, PM_HEIGHT, 0, 0); }
    int GetRow(int row, std::string& out) { 
        return proc(obj, PM_ROW, (void*)row, (void*)&out); 
    }
};

基礎圖片實現:CPicture


class CPicture : public PictureMsg {
    const char** data; int rows, width;
    static int HandleMsg(void* obj, int msg, void* p1, void* p2) {
        CPicture* self = (CPicture*)obj;
        switch (msg) {
            case PM_WIDTH:  return self->width;
            case PM_HEIGHT: return self->rows;
            case PM_ROW: {
                int r = (int)p1; std::string& out = *(std::string*)p2;
                if (r >= self->rows) return 0;
                out = self->data[r];
                out.append(self->width - out.length(), ' '); // 補齊寬度
                return 0;
            }
        }
        return 0;
    }
public:
    CPicture(const char* d[], int n) : data(d), rows(n), width(0) {
        for (int i=0;i

裝飾器:邊框


class CFrameDecorater {
    PictureMsg prev; // 被裝飾對象的介面副本
    static int HandleMsg(void* obj, int msg, void* p1, void* p2) {
        CFrameDecorater* self = (CFrameDecorater*)obj;
        PictureMsg& p = self->prev;
        switch (msg) {
            case PM_WIDTH:  return p.GetWidth() + 2;
            case PM_HEIGHT: return p.GetHeight() + 2;
            case PM_ROW: {
                int r = (int)p1; std::string& out = *(std::string*)p2;
                if (r == 0 || r == p.GetHeight()+1) { // 頂/底邊框
                    out = "+" + std::string(p.GetWidth(), '-') + "+";
                } else { // 中間行
                    std::string mid; p.GetRow(r-1, mid);
                    out = "|" + mid + "|";
                }
                return 0;
            }
        }
        return 0;
    }
public:
    CFrameDecorater(PictureMsg& imp) : prev(imp) {
        imp.obj = this; imp.proc = HandleMsg; // 劫持介面
    }
};

裝飾器:水平拼接


class CHorseDecorater {
    PictureMsg left, right;
    static void GetRowSafe(PictureMsg& p, int r, std::string& out) {
        if (r < p.GetHeight()) p.GetRow(r, out);
        else out = std::string(p.GetWidth(), ' ');
    }
    static int HandleMsg(void* obj, int msg, void* p1, void* p2) {
        CHorseDecorater* self = (CHorseDecorater*)obj;
        switch (msg) {
            case PM_WIDTH:  return self->left.GetWidth() + self->right.GetWidth();
            case PM_HEIGHT: return max(self->left.GetHeight(), self->right.GetHeight());
            case PM_ROW: {
                int r = (int)p1; std::string& out = *(std::string*)p2;
                std::string l, rstr;
                GetRowSafe(self->left, r, l);
                GetRowSafe(self->right, r, rstr);
                out = l + rstr;
                return 0;
            }
        }
        return 0;
    }
public:
    CHorseDecorater(PictureMsg& l, PictureMsg& r) : left(l), right(r) {
        l.obj = this; l.proc = HandleMsg; // 劫持左介面作為結果介面
    }
};

輸出驅動:統一的 operator<<


std::ostream& operator<<(std::ostream& os, PictureMsg& msg) {
    for (int r=0; r

客戶端代碼:極簡調用


int main() {
    const char* pic1[] = {"Paris", "in the", "Spring", "HaHa"};
    CPicture p1(pic1, 3);           // 基礎圖片
    CFrameDecorater f1(p1);         // 加邊框
    CFrameDecorater f2(p1);         // 再加一層邊框
    CPicture p2(pic1, 4);           // 另一張圖
    CHorseDecorater h(p1, p2);      // 水平拼接
    CFrameDecorater fh(p1);         // 給拼接結果加框
    CHorseDecorater h2(p2, p1);     // 反向拼接
    CFrameDecorater f3(p2);
    CHorseDecorater h3(p1, p2);
    std::cout << p1; // 輸出最終結果(p1 介面已被多次劫持,指向最後裝飾層)
    return 0;
}

關鍵洞察:介面劫持

每次建立裝飾器時:
`decorator.constructor(prev_interface) { prev_interface.obj = this; prev_interface.proc = MyHandler; }`
將**被裝飾者的介面結構體**直接改寫,使其指向新裝飾層處理函數。原圖片物件 `p1` **完全不知情**,仍保持原樣,但其 `PictureMsg` 介面已被重導向至最外層裝飾鏈。這就是「不管八風亂起,我自巋然不動」。

與傳統設計模式對比

維度消息驅動裝飾/組合模式
代碼量~100 行~300+ 行
繼承/虛函數無大量
間接層單層函數指標調用多層虛函數表查找
組合靈活性任意動態重組靜態類型綁定
本質攔截器鏈物件包裝樹

總結

**消息驅動即設計模式的本質實現**。Windows 視窗程序 `WndProc(hwnd, msg, wParam, lParam)` 早已證明:單一消息分發函數足以表達所有 GUI 行為變化。C++ 沉思錄練習題的「另解」,不過是將這一經典模式應用於領域模型罷了。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved