关于工作和读书的笔记
[原创文章欢迎转载,但请保留作者信息]Justin 于 2010-01-24
前面已经学过,用虚函数来实现接口是再自然不过的事情。这里要说的是,除了直接单纯使用虚函数机制,还有一些别的方法。
大师说的第一种,是模板模式,利用非虚拟接口函数(Non-Virtual Interface, NVI)来实现。此模板(设计模式中的模板方法,template method)非彼模板(C++中的模板,template),而且也没那么难:
在父类中提供一个公有函数(接口),然后在其中调用真正干活的私有虚函数(默认实现);子类对象通过公有继承自然得到了这个函数(接口),如果子类没有编写自己的实现版本,最终执行的就是默认的实现;如果子类实现了自己的版本,调用的就是子类的版本。(是不是和上节课的1.5有点亲戚关系?) class AClass { public : void interface () { printf( " in base::interface()\n " ); do_interface(); } private : // the default implementation virtual void do_interface() { printf( " in AClass::do_interface()\n " ); } } ; class AClassDerived : public AClass { private : /**/ /* derived can has its own implementation, but not a must.. virtual void do_interface() { printf("in AClassDerived::do_interface()\n"); } */ } ; 【给自己的】如果看到这里还会想:为什么是私有的虚函数?就搜“与大虾对话:领悟设计模式”复习复习……
在父类中提供一个公有函数(接口),然后在其中调用真正干活的私有虚函数(默认实现);子类对象通过公有继承自然得到了这个函数(接口),如果子类没有编写自己的实现版本,最终执行的就是默认的实现;如果子类实现了自己的版本,调用的就是子类的版本。(是不是和上节课的1.5有点亲戚关系?)
看了第一种方法的大多数同学都会有这种想法:这不还是要用虚函数么?穿个wrapper的马甲就认不出你了?大师马上说第二个替代产品是策略模板(strategy pattern),利用函数指针实现:
这种方法的实质,就是把接口函数的实现拿到了类之外。类之中只声明接口的形式,只定义一个函数指针。真正干活的函数(实现)都不是类的成员。这样做带来了一定的灵活性,具体采用哪种实现与类的继承关系是独立无关联的;同时,非类成员函数也有局限性:无法访问类的非公有成员。如果把函数定义为友元或利用公有函数输出私有成员,又会破坏原设计的 封装。如下代码所示: class AClass { public : typedef void * (Interface)( /**/ /* param.. */ ); explicit AClass( Interface pint = defaultInterface) : pInterface(pint) {} // .. private : Interface pInterface; // .. } ; 在构造AClass对象的时候即可指定Interface的真身,虽然,它无法直接访问AClass的非公有成员。
这种方法的实质,就是把接口函数的实现拿到了类之外。类之中只声明接口的形式,只定义一个函数指针。真正干活的函数(实现)都不是类的成员。这样做带来了一定的灵活性,具体采用哪种实现与类的继承关系是独立无关联的;同时,非类成员函数也有局限性:无法访问类的非公有成员。如果把函数定义为友元或利用公有函数输出私有成员,又会破坏原设计的 封装。如下代码所示:
在构造AClass对象的时候即可指定Interface的真身,虽然,它无法直接访问AClass的非公有成员。
估计大师也觉得指针在C++里简单一些,于是更推崇用C++的库(如TR1中的function)来管理接口函数。
原理和函数指针是一样的,只不过因为用了对象来管理资源,使得应用更加灵活。当然,要付出更多一点的代码体积和运行时间代价。 class AClass { // all are the same with the funtion pointer version // except for: typedef std::tr1::function void ( /**/ /* param.. */ ) > Interface; // .. } ;
原理和函数指针是一样的,只不过因为用了对象来管理资源,使得应用更加灵活。当然,要付出更多一点的代码体积和运行时间代价。
大师在最后才说出了最经典的策略模式实现,也是我觉得比较漂亮且容易理解的实现方式。
用两个类搞定: class AInterface{ public : // .. virtual void DoInterface( /* param.. */ ); // .. };AInterface defaultInterface; class AClass{ public : explicit AClass(AInterface * pinter = & defaultInterface) : pInter(pinter) {} void TryInterface() { pInter -> DoInterface(); } // .. private : pInterface * pInter; // .. };
用两个类搞定:
看到最后,似乎本课的思想就是用模式设计(template pattern或strategy pattern)来代替简单的虚函数设计。但是,大师临走前又说了:以上只是举例,为的是说明其实除了简单的虚函数外,还有很多种可能的方式来替代它完成设计需要。
Copyright @ Justin.H Powered by: .Text and ASP.NET Theme by: .NET Monster