C 設計模式:裝飾模式(轉載)
最近在公司分享了下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. 擴充卡模式的用意是改變對象的接口而不一定改變對象的性能,而裝飾模式的用意是保持接口并增加對象的職責。
最近在公司分享了下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. 裝飾模式中使用繼承的關鍵是想達到裝飾者和被裝飾對象的類型比對,而不是獲得其行為。