组装你的类(一):编译期的装饰模式.

     设计模式中讲到的装饰模式可以用来为某些函数动态的配置行为.追究其本质实际上是利用了一个链表结构保存了一组相同签名的函数指针(或者虚基类指针),然后统一调用它们.调整这个链表结构中函数指针的数目和顺序就可以动态的改变该函数的行为.

    但运行期的装饰模式只能为一个类的成员函数添加行为,但不能增加一个新的函数.那么我们如何实现灵活的配置一个类,为类添加函数呢? 这样的功能只有在编译期才能提供(运行期肯定无法动态的添加函数了). 这里我们可以使用一些模板的技巧来组装我们的类.

    下面我们考虑如何把各个函数拆分开,然后再将它们组装在一起. 首先可以想到的是继承关系:继承除了带参的构造函数(不包括拷贝构造函数)和各种赋值函数(operator =). 而对于赋值函数可以简单的在子类利用using operator=;引入.

    这样我们完全可以利用继承的方法将各个类组装起来,最底层的子类则包含了所有的方法.但单纯的继承会出现很多问题,考虑如下情况:

   如果我们这样来实现继承结构,实际上并不能灵活的组装各个功能函数.例如我们需要一个类,其中只希望有大小比较操作(CompareOperator提供)以及相等比较操作(EquationOperator).而出于某些原因不想为用户提供数值计算的操作(CalculateOperator提供). 这样的继承体系根本无法实现这种灵活的代码组装的需求.

 

    我们再考虑一下设计模式中提到的装饰模式的本质:用一个链表保存了一组函数指针.如果我们能实现一个编译期的链表保存下所有的功能类,然后再提供一个操作把这些类按照顺序利用继承的方法组装其他,就可以实现编译期的装饰模式.

    利用模板的一点小技巧就可以实现这个功能:

    这里模板类继承了自己的参数,而这个参数很可能又是一个装饰器,而且它自己又继承自其它的装饰器. 下面我们利用这种方法对上面提供的例子进行改造:

这样我们就可以方便的进行组装了:

这里就组装了一个MyData的类,其中以BaseData为数据基础,并包含了CompareOperator, CalculateOperator, EquationOperator的所有操作.如果我们想定义一个类,只支持EquationOperator和CompareOperator的话,我们可以这样写:

这样就可以方便的完成类的组装操作.

 

但是这个结构还存在一些问题.首先碰到的问题是我们进行代码组装实际是通过单继承来实现的.但在单继承中有两种函数不会被继承:非默认的构造函数(除了无参构造函数和拷贝构造函数以外用户自定义的构造函数)和各种operator=函数都不会被继承.

例如我们为BaseData添加几个函数:

即使添加了这个构造函数和赋值函数,在组装后的类中仍然是看不到的(继承的时候自动屏蔽了这些函数).例如我们希望写如下代码:

编译器会抱怨说MyData类型不能接受一个int的参数.对于赋值函数很简单,我们可以为每一个装饰类写上一句:using Decorator::operator =;就可以将所有赋值函数引入.然后我们仍然可以重写自己的赋值函数.

对于构造函数则没有太好的办法,手工重写吧!改进后的各个基类和装饰类如下:

这样我们写出如下代码的时候编译器不再会抱怨了:

但还有个问题是,像写出这样代码的时候又会出问题:

编译器会抱怨说一个CalculateOperator<Decorator>类型不能赋值给一个EquationOperator<Decorator>的类型.

出现这个问题的原因很简单,operator+=这个函数是CalculateOperator类提供的,它的返回值也是一个CalculateOperator.而最终MyData实际上是一个EquationOperator类的对象.

要解决这个问题我们可以提供一个像上的转换构造函数来解决,大致方法如下:

这样如果ClassA只是一个功能类(没有自己的数据成员),那么就可以安全的提供这样一个函数来进行自动转换,没有任何问题的.但是注意如果你的装饰类中定义了自己的成员变量,则最好就不要提供自动向上转换的函数.下面是改进后的装饰类:

现在,写出如下代码都是没有问题的了:

前面我们只是讨论了如何为一个基类添加方法.下面我们再讨论一下如果利用装饰类加入新的成员变量(以及上面的一组操作).考虑如下的类:

该装饰类提供了一个新的成员变量,并且覆盖了父装饰类中的一些方法.我们仍然可以对类进行灵活的组装.只要保证各个装饰类出现的顺序就可以了,例如:

这样我们可以写出如下代码:

但是当使用MyData3类型进行+=操作的时候编译器会报错:

这正是我们希望的行为:MyData3实际是对MyOtherData的一个包装,而MyOtherData不支持+=操作.我们现在在编译期就检查出来了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值