与大虾对话: 领悟设计模式(2)

2008-04-09 04:29:05来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折



我点点头,告诉她我懂了,然后追问道:“那么public virtual function呢?”

“尽可能不要使用public virtual function。”她拿起一支笔写下了以下代码:

class HardToExtend
{
public:
virtual void f();
};
void HardToExtend::f()
{
// Perform a specific action
}
“假设你发布了这个类。在写第二版时,需求有所变化,你必须改用Template Method。可是这根本不可能,你知道为什么?”

“呃,这个...,不知道。”

“由两种可能的办法。其一,将f()的实现代码转移到一个新的函数中,然后将f()本身设为non-virtual的:

class HardToExtend
{
// possibly protected
virtual void do_f();
public:
void f();
};
void HardToExtend::f()
{
// pre-processing
do_f();
// post-processing
}
void HardToExtend::do_f()
{
// Perform a specific action
}

然而你原来写的派生类都是企图override函数f()而不是do_f()的,你必须改变所有的派生类实现,只要你错过了一个类,你的类层次就会染上先知Meyers所说的‘精神分裂的行径’。” [译者注:参见Scott Meyers,Effective C , Item 37,绝对不要重新定义继承而来的非虚拟函数]

“另一种办法是将f()移到private区域,引入一个新的non-virtual函数:”

class HardToExtend
{
// possibly protected
virtual void f();
public:
void call_f();
};
“这会导致无数令人头痛的问题。首先,所有的客户都企图调用f()而不是call_f(),现在它们的代码都不能编译了。更有甚者,大部分派生类都回把f()放在public区域中,这样直接使用派生类的用户可以访问到你本来想保护的细节。”

“对待虚函数要象对待数据成员一样,把它们设为private的,直到设计上要求使用更宽松的访问控制再来调整。要知道由private入public易,由public入private难啊!”

[译者注:这篇文章所表达的思想具有一定的颠覆性,因为我们太容易在基类中设置public virtual function了,Java中甚至专门为这种做法建立了interface机制,现在竟然说这不好!一时间真是接受不了。但是仔细体会作者的意思,他并不是一般地反对public virtual function,只是在template method大背景下给出上述原则。虽然这个原则在一般的设计中也是值得考虑的,但是主要的应用领域还是在template method模式中。当然,template method是一种非常有用和常用的模式,因此也决定了本文提出的原则具有广泛的意义。]

----------------------------------------------------------------

II. Visitor模式

我正在为一个设计问题苦恼。试用期快结束了,我希望自己解决这个问题,来证明自己的进步。每个人都记得自己的第一份工作吧,也都应该知道在这个时候把活儿做好是多么的重要!我亲眼看到其他的新雇员没有过完试用期就被炒了鱿鱼,就是因为他们不懂得如何对付那个大虾...,别误会,我不是说她不好,她是我见过最棒的程序员,可就是有点刻薄古怪...。现在我拜她为师,不为别的,就是因为我十分希望能达到她那个高度。

我想在一个类层次(class hierarchy)中增加一个新的虚函数,但是这个类层次是由另外一帮人维护的,其他人碰都不能碰:

class Personnel
{
public:
virtual void Pay ( /*...*/ ) = 0;
virtual void Promote( /*...*/ ) = 0;
virtual void Accept ( PersonnelV& ) = 0;
// ... other functions ...
};

class Officer : public Personnel { /* override virtuals */ };
class Captain : public Officer { /* override virtuals */ };
class First : public Officer { /* override virtuals */ };
我想要一个函数,如果对象是船长(Captain)就这么做,如果是大副(First Officer)就那么做。Virtual function正是解决之道,在Personnel或者Officer中声明它,而在Captain和First覆盖(override)它。

糟糕的是,我不能增加这么一个虚函数。我知道可以用RTTI给出一个解决方案:

void f( Officer &o )
{
if( dynamic_cast<Captain*>(&o) )
/* do one thing */
else if( dynamic_cast<First*>(&o) )
/* do another thing */
}

int main()
{
Captain k;
First s;
f( k );
f( s );
}
但是我知道使用RTTI是公司编码标准所排斥的行为,我对自己说:“是的,虽然我以前不喜欢RTTI,但是这回我得改变对它的看法了。很显然,除了使用RTTI,别无它法。”

“任何问题都可以通过增加间接层次的方法解决。”

我噌地一下跳起来,那是大虾的声音,她不知道什么时候跑到我背后,“啊哟,您吓了我一跳...您刚才说什么?”

“任何问...”

“是的,我听清楚了,”我也不知道哪来的勇气,居然敢打断她,“我只是不知道您从哪冒出来的。”其实这话只不过是掩饰我内心的慌张。

“哈,算了吧,小菜鸟,”大虾斜着眼看着我,“你以为我不知道你心里想什么!”她把声音提高了八度,直盯着我,“那些可怜的C语言门徒才会使用switch语句处理不同的对象类型。你看:”

/* A not-atypical C program */
void f(struct someStruct *s)
{
switch(s->type) {
case APPLE:
/* do one thing */
break;
case ORANGE:
/* do another thing */
break;
/* ... etc. ... */
}
}
“这些人学习Stroustrup教主的C 语言时,最重要的事情就是学习如何设计好的类层次。”

“没错,”我又一次打断她,迫不及待地想让Wendy明白,我还是有两下子的,“他们应该设计一个Fruit基类,派生出Apple和Orange,用virtual function来作具体的事情。

“很好,小菜鸟。C语言门徒通常老习惯改不掉。但是,你应该知道,通过使用virtual function,你增加了一个间接层次。”她放下笔,“你所需要的不就是一个新的虚函数吗?”

“是的。可是我没有权力这么干。”

“因为你无权修改类层次,对吧!”

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:一个画渐变的方法

下一篇:2002-01-08 Borland宣布Borland Enterprise Studio for Windows