手机站
网通分站
电信主站
密 码:
用户名:
当前位置 : 主页>程序设计>C/C++>列表

乌托邦式的接口和实现分离技术

来源:互联网 作者:west263.com 时间:2008-02-23
西部数码-全国虚拟主机10强!40余项虚拟主机管理功能,全国领先!双线多线虚拟主机南北访问畅通无阻!免费赠送企业邮局,.CN域名,自助建站480元起,免费试用7天,满意再付款! P4主机租用799元/月.月付免压金!

  假如您没有选择virtual,那么IRWiter被派生后,那么派生类的继承树中可能存在多个IReader实现,假如这个派生类需要只能提供一份IReader的语义怎么办?除了重新实现接口还能怎样?反过来,假如我们选择了virtual继承,那么派生类需要多个实现怎么办?真是个麻烦事。“这是典型的过度设计,我们为什么要考虑这么多?”您能够这么说,但事实上,即使是个数百文档的小型系统,也完全可能迫使您作出选择。虽然,我们仍然有办法作出挽救措施,但是也只是苟延残喘而已。正如我前面所说,这是个危险的道路,聪明如您,是断然不会让自己陷入这样的泥潭的。

  让我们离开虚拟继承,先回到重复代码的问题上来。有没有更好的解决办法呢?还好,在C 的世界里,我们有神奇的template,让我们来消除重复的代码:

template<typename Base>
class ImpReader : public Base{
constraint(is_base_derive(IReader, Base))
Implementation IReader
<!--[if !supportEmptyParas]--> };
class HeapReader : ImpReader<IReader>{};
class StackReader : ImpReader <IReader>{
virtual void dispose() {};
<!--[if !supportEmptyParas]--> };

  请注意,我们还是假设IRefCount已提供了一个默认实现。现在,情况好了很多,任何的代码都只有一份,而且,概念也没有被破坏。假设,Writer也同样需要类似的能力,那么,我们又多了StackWriter和HeapWriter.事实上,真的有人用到了StackWriter吗?我不知道,只是,提供了StackReader,没有理由不提供StackWriter啊。让我们继续。

  现在,我们发现,需要改进内存分配的性能问题,于是,我们希望通过内存池来分配对象,相应的dispose也需要修改:

virtual void dispose(){ distory(this);}

  于是,我们又多出两个类,PoolReader和PoolWriter。这真是糟糕,组合爆炸可不是什么好兆头。

  从我们前述的变化来看,都是IRefCount在变化,为什么不把这种变化分离出来呢?不必为IRefCount提供默认实现,借鉴ImpReader的手法:

template<typename Base>
class ImpHeapRefCount : public Base{
constraint(is_base_derive(IRefCount, Base));
..};

  类似的:

template<typename Base> class ImpStackRefCount : public Base;
<!--[if !supportEmptyParas]--> template<typename Base> class ImpPoolRefCount : public Base; <!--[endif]-->

  再看看,我们如何实现任何的Reader.

typedef ImpReader< ImpHeapRefCount<IReader> > HeapReader;
typedef ImpReader< ImpStackRefCount<IReader> > StackReader;
typedef ImpReader< ImpPoolRefCount<IReader> > PoolReader;

  以HeapReader为例,实际的继承关系是这样的:

ImpReaderàImpHeapRefCountàIReaderàIRefCount;

  对于Writer,我们完万能够采取同样的手法来实现。对于上述的typedef能够预先定义,也完万能够不定义,交给最终用户去组装吧。现在,类的设计者再也不必为选择实现而痛苦了,您只要提供不同的砖头,客户程式员能够轻而易举的建立起大厦。更有比这更让一个设计师幸福的吗?

  继续深入,考察ImpHeapRefCount和ImpStackRefCount的实现,我们提到,dispose方法的实现是不相同的,但是,其他部分:add,releasee和count的实现完万能够相同。然而我们现在又分别实现了一遍,为了不违背DRY原则,我们如下处理:

template<typename Base>
class ImpPartialRefCount : public Base{
//实现add, release和count.
};

template<typename Base>
class ImpHeapRefCount : public Base{
virtual void dispose() { delete this;}
};

template<typename Base>
class ImpStackRefCount : public Base{
virtual void dispose() { }
};

  然后,我们能够这样定义Reader:

typedef ImpReader<ImpHeapRefCount<ImpPartialRefCount<Ireader> > > HeapReader;

  请注意,我们在这里展示了一种能力,不必在一个实现当中完整的实现整个接口,能够把一个接口的实现分拆到多个实现当中。这个能力是非凡的,借助于此,我们能够提供更小粒度的实现单位,给最终用户来组装。具体拆分到什么样的程度完全取决于代码复用的需求,连同概念维护的需要。

  我们提供了高度的复用能力,同时避免了继承带来的强耦合,连同对推迟设计决策的支持,这些能力对于软件设计师而言,正如Matthew在《Imperfect C 》中所说的,这简直就是现实中的乌托邦!

  现在我们把这种手法首先针对单继承做一个小结。对于任意的接口IInterface,我们提供如下的实现:

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

template<typename Base>
class ImpInterface : public Base{
constraint(is_base_derive(IInterface, Base));
};
Google