原则35:考虑virtual函数以外的其他选择

本原则所叙述的内容比较复杂涉及到设计模式相关的内容。它主要讲的就是以non-virtual函数代替virtual函数的策略。

作者举了一个例子,说有这么一个游戏的血量计算函数,但是你知道啊,这个游戏里面有很多不同的怪物,像什么石头人、萨特一家、人马等,它们计算血量的方式是不同的。那就涉及到一个多态继承的问题了,就是父类中有一个virtual函数,不同的子类去重写这个virtual函数,如果不重写那也就只有继承父类的默认实现了,毕竟virtual函数存在的意义就在于此啊。

作者在这里没有提及为啥要寻找一个方案去替代virtual函数,而是直接说要去寻找一个替代方案,这不免有些让人不解。很显然作者的态度就是不解释。不过经过阅读我大概了解到作者就是想另辟蹊径,就是想脱离OOP的老套路,我想这是不是有点哗众取宠呢,不过他的方案的确有可取之处。

在这里作者推荐了第一种解决方案——用NVI(Non-VirtualInterface)去实现TemplateMethod模式。在这个过程中,作者讲了一个他的前人的方法,他的前人们认为,所有的virtual函数都应该是private的,这个是前提。为了实现这种前提,他的前人们决定使用一个非virtual公有成员函数接口,然后让这个公有接口去调用它内部的私有virtual函数。那么这种通过non-virtual函数接口去掉用privatevirtual函数的手法就是所谓的NVI了,而且这个NVI还属于什么TemplateMode模式的范畴。而这个non-virtual的公有成员函数接口被称为wrapper(外覆器)。

在这种设计模式下,多数子类都会重新定义父类中的virtual函数,因为这是它们自身特定的实现代码,但是父类中的virtual函数不排除被子类调用的可能。

不过,NVI父类中的virtual不一定非要是private的,这个要根据场合而定。例如有些继承体系要求要在子类的virtual函数的实现中去调用父类的virtual函数,那如果父类的virtual函数还是private的,那这条路就行不通了,此时父类的virtual函数就应该是protected的。再比如,具备多态性质的集成体系中,父类的析构函数就必须是virtual的并且是public的,因为此类对象需要调用父类的析构函数去析构父类成分。因为如果直接调用virtual函数的话,必要的事前和事后工作就不一定能做的了。而如果virtual函数是被调用的函数,那么在主调函数中就可以做一些事前工作和事后工作了,这就是采用NVI方法的原因。

在这个原则当中作者是在类内部直接去实现成员函数的,这样一来这个成员函数就是内联函数了,这涉及到了原则30的一些内容。在类体外定义的函数要加inline关键字才能变成内联函数,但是函数定义中含有循环、switch、goto、static或者该函数是递归函数,那么编译器就不允许它成为内联函数。

第二方法是用函数指针FunctionPointers去实现Strategy模式从而绕过virtual函数。还是先前那个例子,但是换了一个角度。反正都是计算怪物的血量嘛,那么我们何不抽取其共性做成一个函数,然后弄一个接口去接受指向这个函数的指针呢。

首先一个怪物肯定属于一个类,这个怪物有一个血量的属性,而这个血量的属性又属于另一个类。那么就可以把这个血量类的对象设成是这个怪物类的一个私有成员。这个怪物类的构造函数可以接受一个指向默认生命值计算的函数的指针作为缺省参数,就是如果有自己的生命值计算函数就用自己的,没有就用默认的。再用这个指针去给这个血量对象进行初始化,最后还是提供若干个公有接口返回这个血量对象,而这若干个接口分别表示若干种不同情况的血量。这个什么Strategy模式到底是咋回事,我现在还不明白,这里只不过是一个大致的推测而已。

那这种方式的好处是什么呢?那就是血量的计算方式和具体的对象之间并无绑定关系,使用起来相对灵活自由。另外,如果这个怪物类提供了一个设置计算血量的setXXX函数,还可以实现在运行期改变血量计算方式。

从封装性的角度讲,这个独立的血量计算函数与怪物类是相对独立的,那么这个函数就不会去访问怪物类的非public成分。但是有时你为了让血量计算函数得以执行就需要更多的信息,而这些信息是从public接口中获得的,所以可能会遇到弱化封装性的要求。

第三种方式是用trl::function来完成Strategy模式。这个trl::function是个神马玩意以我现在的见识来看还真不知道,不过从作者的描述来看,这个东西是一个容器,它可以装载函数、函数对象、成员函数指针等,这样一来它包容的空间就大了许多。trl::function的好处可兼容能通过隐式转换转换成参数函数的参数,并且也能像参数中那个函数一样提供一个返回值,但是它的兼容性更好,也就是提供了一种需求上的弹性。

在这里作者又说了一大堆有关trl::bind的内容,在这里我只大致说一下我的理解就好。这个trl::bind就是能将某个参数与接受这个参数的函数绑定,并且是以明确的方式绑定。其他的我就真不知道了。

说了这么多那到底为什么要去实现Strategy模式呢?因为在Strategy模式中血量计算的部分与游戏人物的类是相对独立的,这样你怎么改动血量计算的方式游戏人物类面对的也只是一个单一的接口。这样血量计算类就可以担负起解决virtual函数的问题的重任了,这是一种问题转移的策略。也是解决virtual函数问题的第四种方式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容