您的位置:首页 > 其它

设计模式研究:如何判断一个封装方案是好是坏

2006-12-20 16:53 639 查看
学习设计模式的人常说“封装变化”。变化,应当怎么封装?设计模式那么多种,可以采用多种封装方案,如何判断一个封装方案是好是坏?
答案:看封装后系统演化时,修改点是否唯一而定。
说得直白一些。当系统需要扩展一个功能时,你要修改的地方是多了还是少了?少了就说明封装得好。
封装的最高境界是:以后增加一个功能,只需要修改一个地方就行。
下面,我举一个封装失败的例子来说明这一点。

此例子来自一个实际应用的P2P网络应用项目。在网络项目中,需要定义多种协议数据包,以此来互相通讯。这里的变化趋势是很明显的:随着系统的发展,数据包的数量会有增减,原有数据包的字段也有可能增删。
现在的问题是,如何将这些变化封装起来?下面是一个程序员实际使用的方案。

1. 最初编写网络程序,以结构体来进行协议编码,解码
struct GPRSInboundMsg
{
PROTO_HDR Header;

UBYTE_t arUnitAddr[UNITADDR];
INT32_t iEventTime;
UBYTE_t pMessage;
};

2. 这种协议是比较常见协议,带有基本数据类型,数组,和不定长的成员,如果直接使用结构体进行协议数据的编码,解码,很容易因为字符串拷贝,内存申请,释放这些常用操作带来问题, 下面使用类来封装该协议:

class CPtoOutbMsg{
public:
void SetUnitAddr(const BYTE_t *pAddr);
void SetMessage(const BYTE *pMsg);
virtual int Pack(char *pPointer, const char usBufLen);
virtual int UnPack(const char *pPointer, const char usBufLen);
private:
PROTO_HDR Header;
UBYTE_t arUnitAddr[UNITADDR];
INT32_t iEventTime;
UBYTE_t pMessage;
};

这个方案很简单,就是用对象来代替结构体,并且将结构体的成员全用函数封装起来。
设计者解释他这样做的目的是,避免直接操作这些字段引起的内存越界等问题。从这个角度来说,设计者这样的封装是达到目的了。可惜的是,设计者忘记了封装的目的:减少修改点。
我们看看这种封装方案对系统的影响。
直接使用结构体的方案时,某个数据包增删一个字段,需要修改的地方有:
1、修改结构体;
2、修改所有引用该结构体的地方(删除字段需要,增加字段就不用了。但如果系统有某些特殊需求,如计算可变长度数据包的实际长度,那么这些地方也可能要修改。)
而经过上面封装之后,对于同样的需求,需要修改的地方有:
1、修改结构体(同上);
2、修改所有引用该结构体的地方(同上)
3、修改该数据包对象的函数(增删两个该字段相应的读写函数)
4、修改Pack函数(打包函数)
5、修改UnPack函数(解包函数)
增加数据包也是类似的,即除了增加相应结构体之外,需要相应创建一个结构体封装对象:每个字段的读写函数,打包解包函数等等。
现在很清楚了,这种封装是失败的。它为系统修改带来了不止一倍的开销。
使用这样的封装,带来的后果是比较严重的,每次协议修改,都会让修改者累个半死(想想一个字段的变动,要修改五个地方!)。程序员必定强烈反对修改协议,或者产生怠工情绪。

最后总结:
一个封装方案成功与否,看封装后系统演化时,修改点是否唯一而定。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐