最近在公司分享了下C語言版的設計模式,記錄一下吧。
參考:《設計模式之禪》中“裝飾模式”章節。
上面書中是用C++來實現的,我使用了書中的例子,改用C語言來實現。
一、基礎知識
面向對象最重要的三個特性,在C語言中大致的實現如下所示。
1 //1、繼承性 2 3 typedef struct _Parent 4 { 5 int data_parent; 6 }Parent; 7 8 typedef struct _Child 9 { 10 struct _Parent parent; 11 int data_child; 12 }Child; 13 14 //2、封裝性 15 16 struct _Data; 17 18 typedef void (*fProc)(struct _Data* pData); 19 20 typedef struct _Data 21 { 22 int value; 23 fProc Process; 24 }Data; 25 26 //3、多態 27 28 typedef struct _Run 29 { 30 void* pData; 31 void (*fProc)(struct _Run* pRun); 32 }Run;
裝飾模式,在C語言中的實現:
1 typedef struct _Object 2 { 3 struct _Object* prev; 4 5 void (*decorate)(struct _Object* pObject); 6 }Object; 7 8 void decorate(struct _Object* pObeject) 9 { 10 assert(NULL != pObject); 11 12 if(NULL != pObject->prev) 13 pObject->prev->decorate(pObject->prev); 14 15 printf("normal decorate!\n"); 16 }
二、講個故事
很久很久以前,大概還是小學的時候,期末考試完,最可怕的事情就是:老師讓把試卷拿回家里,讓家長簽字。
1 typedef struct _SchoolRpt 2 { 3 void (*Report)(); 4 void (*Sign)(char* name); 5 } 6 7 void Report() 8 { 9 printf("我這次考了:語文62 數學65 體育98 自然63 \n"); 10 } 11 12 void Sign(char* name) 13 { 14 printf("家長簽名:%s \n", name); 15 } 16 17 int main() 18 { 19 SchoolRpt sr = {Report, Sign}; 20 21 sr.Report(); 22 23 return 0; 24 } 25 26 /* 27 Output: 28 29 我這次考了:語文62 數學65 體育98 自然63 30 31 ////////////挨打中///////////// 32 */
得,這么耿直的直接拿給老爹看,一定得被啪啪啪。還想要簽字?做夢!
三、故事還得繼續
得想個辦法呀。這個事情,跟寫作文一樣,得潤色潤色,不能太耿直。
這樣,先告訴老爹這次最高分時多少,“看,最高分也不高,大家考的都不好,題難呢!”。
然后再說自己的成績。
最后,再說下自己的班級排名,“其實,排名比往常還有點小進步的哦~”。
好辦法,我們試一試。
1 typedef struct _SchoolRpt 2 { 3 void (*Report)(); 4 void (*Sign)(char* name); 5 } 6 7 void Report() 8 { 9 printf("我這次考了:語文62 數學65 體育98 自然63 \n"); 10 } 11 12 void ReportHighScore() 13 { 14 printf("最高分:語文82 數學81 體育100 自然79 \n"); 15 } 16 17 void ReportSort() 18 { 19 printf("排名:30 \n"); 20 } 21 22 void Sign(char* name) 23 { 24 printf("家長簽名:%s \n", name); 25 } 26 27 int main() 28 { 29 SchoolRpt sr = {Report, Sign}; 30 31 ReportHighScore(); 32 sr.Report(); 33 ReportSort(); 34 35 sr.Sign("FATHER"); 36 37 return 0; 38 } 39 40 /* 41 Output: 42 43 最高分:語文82 數學81 體育100 自然79 44 我這次考了:語文62 數學65 體育98 自然63 45 排名:30 46 家長簽名:FATHER 47 */
好嘞,成了,拿到了老爹的簽名,少挨了一次打。開心~
四、故事有時候會變
但是呢,事情都不是絕對的。
有時候老爹心情比較好,只說了最高成績,老爹就要簽名了,還沒來得及說自己的排名呢。
有時候老爹心情不太好,那得想更多的法子才行。
那得給每種場景都寫個 方法嗎?那得累死啦。
噔噔噔,裝飾模式出場了:
1 //抽象組件 2 typedef struct _iobject 3 { 4 struct _iobject* prev; 5 6 void (*frame_creater)(struct _iobject* obj); //接口函數 7 8 void (*report)(); 9 }Iobject; 10 11 //初始化某個Iobject的變量 12 void init_iobject(Iobject* obj, void (*report)(), void (*frame_creater)(Iobject* m_obj)) 13 { 14 obj->frame_creater = frame_creater; 15 obj->prev = NULL; 16 obj->report = report; 17 } 18 19 //置current的prev值 20 void add_iobject(Iobject* current, Iobject* prev) 21 { 22 current->prev = prev; 23 } 24 25 //接口函數 26 void decorator_frame_creater(struct _iobject* obj) 27 { 28 if(obj->prev != NULL) 29 obj->prev->frame_creater(obj->prev); 30 31 obj->report(); 32 } 33 34 /* ============================================================= */ 35 typedef struct _SchoolRpt 36 { 37 struct _iobject scoreRpt; 38 39 void (*Sign)(char* name); 40 }SchoolRpt; 41 42 void Report() 43 { 44 printf("我這次考了:語文62 數學65 體育98 自然63 \n"); 45 } 46 47 void Sign(char* name) 48 { 49 printf("家長簽名:%s \n", name); 50 } 51 52 //裝飾者 53 Iobject HighScoreDecorator; 54 Iobject SortDecorator; 55 56 void ReportHighScore() 57 { 58 printf("最高分:語文82 數學81 體育100 自然79 \n"); 59 } 60 61 void ReportSort() 62 { 63 printf("排名:30 \n"); 64 } 65 66 int main() 67 { 68 SchoolRpt father = {{}, Sign}; 69 70 //老爹看報告前,得做些准備工作 71 Iobject *sr = &((Iobject)father); 72 73 init_iobject(sr, Report, decorator_frame_creater); 74 init_iobject(&HighScoreDecorator, ReportHighScore, decorator_frame_creater); 75 init_iobject(&SortDecorator, ReportSort, decorator_frame_creater); 76 77 //快加上裝飾 78 add_iobject(&SortDecorator, sr); 79 add_iobject(sr, &HighScoreDecorator); 80 81 //小心翼翼拿給老爹 82 sr->frame_creater(sr); 83 84 //老爹簽名 85 father.Sign("FATHER"); 86 87 return 0; 88 } 89 90 /* 91 Output: 92 93 最高分:語文82 數學81 體育100 自然79 94 我這次考了:語文62 數學65 體育98 自然63 95 排名:30 96 家長簽名:FATHER 97 */
這樣就好了,不同的場景,只需要配置不同的報告方式就好。再也不用擔心老爹揍我啦,哈哈~(假的)。
五、裝飾模式
裝飾者模式
Decorator模式(別名Wrapper):動態將職責附加到對象上,若要擴展功能,裝飾者提供了比繼承更具彈性的代替方案。
意圖:
動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。
設計原則:
1. 多用組合,少用繼承。
利用繼承設計子類的行為,是在編譯時靜態決定的,而且所有的子類都會繼承到相同的行為。然而,如果能夠利用組合的做法擴展對象的行為,就可以在運行時動態地進行擴展。
2. 類應設計的對擴展開放,對修改關閉。
要點:
1. 裝飾者和被裝飾對象有相同的超類型。
2. 可以用一個或多個裝飾者包裝一個對象。
3. 裝飾者可以在所委托被裝飾者的行為之前或之后,加上自己的行為,以達到特定的目的。
4. 對象可以在任何時候被裝飾,所以可以在運行時動態的,不限量的用你喜歡的裝飾者來裝飾對象。
5. 裝飾模式中使用繼承的關鍵是想達到裝飾者和被裝飾對象的類型匹配,而不是獲得其行為。
6. 裝飾者一般對組件的客戶是透明的,除非客戶程序依賴於組件的具體類型。在實際項目中可以根據需要為裝飾者添加新的行為,做到“半透明”裝飾者。
7. 適配器模式的用意是改變對象的接口而不一定改變對象的性能,而裝飾模式的用意是保持接口並增加對象的職責。
