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

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

此例子来自一个实际应用的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函数(解包函数)
增加数据包也是类似的,即除了增加相应结构体之外,需要相应创建一个结构体封装对象:每个字段的读写函数,打包解包函数等等。
现在很清楚了,这种封装是失败的。它为系统修改带来了不止一倍的开销。
使用这样的封装,带来的后果是比较严重的,每次协议修改,都会让修改者累个半死(想想一个字段的变动,要修改五个地方!)。程序员必定强烈反对修改协议,或者产生怠工情绪。

最后总结:
一个封装方案成功与否,看封装后系统演化时,修改点是否唯一而定。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值