最近在公司分享了下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. 适配器模式的用意是改变对象的接口而不一定改变对象的性能,而装饰模式的用意是保持接口并增加对象的职责。