定义
动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案
要点
- 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方案
- 尽量不修改现有的代码,允许扩展代码
- 装饰者模式意味着有很多用来包装具体组件的装饰者类
- 装饰者类可以在被装饰者的行为前面或后面加上自己的行为,甚至替换掉被装饰者的行为来达到自己的目的
- 可以有无数个装饰者包装一个组件
- 在客户程序不依赖于组件的具体类型的情况下,装饰者对组件的客户是透明的
- 装饰者会导致设计中出现许多小的对象,不要过度使用
类图
还是网上下的
白话
不知道小时候有没有玩过一个游戏,游戏玩法就是几个人在一些小纸条上写上各自的名字,然后再在另外的纸条上分别写上时间、地点、和谁、干什么了,最后把这些纸条混在一起,大家随便抽,最后把各自抽到的纸条组在一起,看凑在一起的是什么话,比如有 "张三10点多在屋顶上打飞机"之类的,大家就笑了。。。。
将这个游戏代入到装饰者模式中来,很显然,写在纸上的都是 "汉字"(IObject),它们要"组合"(Method)在一起,要对人物组件 "张三"(object1)、"李四"(object2)用别的字进行包装"组合"(Method)。怎么包装呢,"随机抽"(Decorator)了"组合"(Method)在一起,哪些字可以用来抽列,就是剩下的"时间"(A_Decorator),"地点"(B_Decorator),"干什么"(C_Decorator) ,这些都是可以被抽了"组合"(Method)在一起的。最后调用某个人物的"组合"(Method)方法就成了让大家发笑的一句话了。
代码实现及实例
装饰者模式实际上是一种迭代处理方式,吐槽一下,能不用迭代,尽量不要用迭代。
有个酒店楼层房间管理系统,由上位机和各个楼层的信息监测设备节点组成。设备节点通过网络将数据传送给上位机。需传送的数据有很多类型,而且每种类型又包含很多子类型。截取一部分来实现。
将每次需要传送的数据包统称为一个数据帧。假设有2种数据帧,大的类型分为2个,一是固定长度的帧(Fixed_Frame),二是不固定长度的帧(UnFixed_Frame);小的类型分为4个,确认帧(Confirm_Frame)、参数帧(Param_Frame)、实时数据帧(RTData_Frame)、带时标(TFlag_Frame)
各个类型的代码实现
extern unsigned char frame_buffer[128]; #define START_BYTE 0x68 //抽象组件 typedef struct _iobject { struct _iobject *prev; int cr_data_len; //用于记录数据的默认长度,在初始化obj时赋初值 int data_len; //用于记录已有的obj.cr_data_len之和 int var_data_len; //接口函数中用来存储长度值的变量 int (*frame_creater)(struct _iobject *obj); //接口函数 }Iobject; //具体组件 Iobject Fixed_Frame; //固定帧 Iobject UnFixed_Frame; //可变长度帧 //具体装饰者 Iobject Confirm_Frame; //确认帧 Iobject Param_Frame; //参数帧 Iobject RTData_Frame; //实时数据帧 Iobject TFlag_Frame; //带时标 //抽象的装饰者 Iobject Decorator;
抽象的装饰者Decorator可以直接用下面的decorator_frame_creater函数直接代替,为了形象点, 最后还是加上了
相关函数
//初始化某个Iobject的变量 //参数说明就略了 void init_iobject(Iobject *obj,int cr_data_len,int (*frame_creater)(Iobject *m_obj)) { obj->cr_data_len=cr_data_len; obj->data_len=0; obj->frame_creater=frame_creater; obj->prev=0; } //置current的prev值 void add_iobject(Iobject *current,Iobject *prev) { prev->data_len=current->cr_data_len+current->data_len; current->prev=prev; } //接口函数 int decorator_frame_creater(struct _iobject *obj) { obj->var_data_len=0; if(obj->prev!=0) obj->var_data_len=obj->prev->frame_creater(obj->prev); return obj->var_data_len; } //固定帧的装饰,加了1个开头标识 int Fixed_frame_creater(struct _iobject *obj) { obj->var_data_len=decorator_frame_creater(obj); frame_buffer[obj->data_len]=START_BYTE; return obj->cr_data_len+obj->var_data_len; } //确认帧的装饰,加了2个0 int Confirm_Frame_frame_creater(struct _iobject *obj) { obj->var_data_len=decorator_frame_creater(obj); frame_buffer[obj->data_len]=0x00; frame_buffer[obj->data_len+1]=0x00; return obj->cr_data_len+obj->var_data_len; }
最后是使用
int main(void) { int count=0; init_iobject(&Fixed_Frame,1,Fixed_frame_creater); init_iobject(&Confirm_Frame,2,Confirm_Frame_frame_creater); init_iobject(&Decorator,0,decorator_frame_creater); while(1) { //置发送帧的一些配置选项,假设需要发送一个确认的固定帧 add_iobject(&Decorator,&Fixed_Frame); add_iobject(&Fixed_Frame,&Confirm_Frame); //调用decorator的接口函数 count=Decorator.frame_creater(&Decorator); //网卡发送数据帧 eth_send(frame_buffer,count); } }
可以看到,在维护修改程序时,如果要添加新的帧,就要添加一些的新的iobject结构,少了还好,多了后就很繁琐了,要是内存还很小的话,还耗费内存。另外装饰者模式采用迭代方式,虽然杜绝了死循环的可能,但是在迭代函数的实现中,若是局部变量过多了,也浪费了栈的空间,甚至可能导致栈溢出,个人认为,嵌入式中,少用为好