哲学问题:模板方法 [英] Philosophical question: Template Method

查看:58
本文介绍了哲学问题:模板方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,所以我和我的同事在

工作中遇到了哲学上的分歧。他是模板方法模式的支持者,即:


class foo

{

public:

void bar(){do_bar(); }

受保护:

虚拟空虚do_bar(){}

};


class baz :public foo

{

受保护:

虚拟空虚do_bar(){} //被覆盖

};


正如你所看到的,非虚拟公共方法foo:bar()只存在用于调用虚拟方法do_bar的

,该类的派生类可以覆盖像

常规虚拟方法。


我的同事声称,这种方法的优势在于未来的实现者

可能会改变虚拟do_bar方法的预处理和后处理行为

(例如分别在

调用do_bar之前/之后添加信号量获取/放弃调用)没有改变来自

foo的类。


此外,他声称这决定了制定方法

公共/私人从决定虚拟方法 - 一个重要的

advan他声称,这是一个很好的例子。


他向我指出了关于C ++用户期刊的文章,它似乎支持

模板方法模式,甚至可以用到假设所有公共虚拟

方法都应该被禁止使用模板方法。


参见 http://www.cuj.com/documents/s=8000/cujcexp1812hyslop/


我,另一方面,更喜欢简单的


类foo

{

public:

虚拟空格条(){}

};


class baz:public foo

{

public:

虚拟空栏(){} //被覆盖

};


作为Java的忠实粉丝,我也很欣赏界面模式:


class foo //界面

{

public :

虚拟空格条()= 0;

};


类baz:虚拟公共f oo

{

public:

virtual void bar(){} //实施

};


此外,如果派生类决定它需要为虚拟方法执行不同的操作前后操作,模板方法可能会被破坏......


class foo

{

public:

void bar(){do_bar(); }

受保护:

虚拟空栏(){}

};


class baz :public foo

{

public:

void bar(){frobnicate(); do_bar(); } //我需要不同的东西!

proctected:

虚拟空栏(){} //被覆盖

};


在上面的类层次结构中,两个调用...


baz baz_object;

foo& foo_reference(baz_object);

baz& baz_reference(baz_object);


foo.bar();

baz.bar();


。 ......会产生两种不同的呼叫路径。


当然你可以说不要那样做,但我也可以说但我需要

覆盖此类的默认行为。


你说什么,温和的程序员?模板方法值得麻烦吗?他们将b $ b换成另一类错误吗?更改pre-and $ / $
在基类后操作的便利性实际上掩盖了我们已经设置错误并且真正需要重新检查每个后代的事实class?


模板方法模式与Interface方法不兼容,而不是
,因为它无法完成,但因为没有意义结合它们。空的

非虚拟公共方法,它的工作就是简单地调用纯虚拟的

保护方法 - 这只是愚蠢的。

那么它就是其中之一。什么是更好的设计范例?

解决方案

" Dave Rahardja" < as*@me.com>在消息中写道

新闻:2r ******************************** @ 4ax.com ...

好的,所以我和我的同事在工作中遇到了哲学上的分歧。他是模板方法模式的支持者,即:

class foo
{
公开:
void bar(){do_bar(); }
protected:
virtual void do_bar(){}
};

类baz:public foo
{
protected:
virtual void do_bar(){} //覆盖
};

如您所见,非虚拟公共方法foo:bar()仅存在于
调用虚方法do_bar,类的派生可以像常规虚方法一样覆盖



我将它用于大型层次结构,虽然我不知道它有一个名字(我确实

有设计模式;猜我会必须刷新我的记忆。

我的同事声称,这种方法的优势在于未来的
实现者可能会改变虚拟do_bar的预处理和后处理行为
方法(例如分别在
调用do_bar之前/之后添加信号量获取/放弃调用),而不更改从foo派生
的类。


是的,这是一个原因。

另一个是没有它你的公共界面混乱了

每个派生类中的函数相同。我希望保持最小的公共

界面。如果某个成员在基类中是公开的,那么在其他任何地方都不需要

。所以我使用它即使我无法想象

做更多的事情而不是从公共非虚拟中调用受保护的虚拟。


另一个原因是你可以添加一个断点或一些调试代码到公共函数
;所有的电话都必须通过那里。


由于所有这些原因,我使用这种模式的课程。

此外,他声称这将分开他声称,决定制定公共/私人方法,使决策方法变得虚拟 - 这是一个重要的优势。


我不明白这个原因。我没有影响其访问权限的函数''虚拟性

(在那些我不会因为这种方法而烦恼的小型层次结构中)。<他指的是关于C ++ Users Journal的文章,它似乎支持
模板方法模式,甚至可以说所有公开的
虚拟方法都应该被禁止赞成模板方法。


无需禁止它。尽管我喜欢模板方法,但我不认为

公共虚拟实际上是邪恶的。

参见 http://www.cuj.com/documents/s=8000/cujcexp1812hyslop/
<另一方面,我更喜欢简单的类foo
{
公开:
虚拟空栏(){}
} ;

班巴兹:public foo
{
公开:
虚拟空栏(){} //被覆盖
};
<作为Java的忠实粉丝,我也很欣赏界面模式:

class foo //界面
{
公开:
虚拟空白栏( )= 0;
};

类baz:虚拟公共foo
{
公共:
virtual void bar(){} // implementation
};

此外,如果派生类决定
它需要为虚拟方法进行不同的操作前和操作后,模板方法可能会被破坏... {
公开:
void bar(){do_bar(); }
protected:
virtual void bar(){}


你的意思是:虚拟空虚do_bar(){}?

};

班巴兹:public foo
{
公开:
void bar(){frobnicate(); do_bar(); } //我需要不同的东西!


如果这是适合全班的话。你确定这个函数

应该叫做bar()吗?也许一个不同的名字将更适合

。如果它应该是bar()那么我建议将其设为虚拟

(并保护到处,并添加一个新的公共功能Bar()到基地

类调用bar();即双模板方法!)。

proctected:
virtual void bar(){} //覆盖


virtual void do_bar(){}再次?

};

在上面的类层次结构中,这两个调用......

baz baz_object;
foo& foo_reference(baz_object);
baz& baz_reference(baz_object);

foo.bar();
baz.bar();

...将产生两个不同的调用路径。


是的,这就是为什么我不喜欢它。

当然你可以说不要那样做,但我也可以说但我需要覆盖此类的默认行为。


你还没有解释_not_如何使用模板方法解决你的问题。

所以你怎么说,温柔编码器?模板方法值得麻烦吗?


对于拥有大量公开的大型层次结构,我想是这样的。

他们是
交换另一类错误?


不,除了不得不写一个

的功能外,你不使用它会获得什么?公共非虚拟可以是内联的,所以甚至没有

的性能成本。

是否更改了pre-and
post后的便利性基类的操作实际上掩盖了我们有
的设计错误并且真的需要重新检查每个后代类的事实?


你不可能对此进行概括。这取决于具体的

情况。如果这是使用该方法的原因,我认为使用pre-and $

操作本身没有任何错误。

模板方法模式与之前不兼容接口方法,


我也必须看一下。

不是
因为它不可能完成,但因为将它们组合起来没有意义。
空的非虚拟公共方法,其工作就是简单地调用纯虚拟的受保护方法 - 这只是愚蠢的。


这正是我的工作,我在整个层次结构中有一个不错的小公共接口

,没有性能成本。

那么它就是其中之一。什么是更好的设计范例?




两者。


DW


" tom_usenet" <到******** @ hotmail.com>在消息中写道

news:3f **************** @ news.easynet.co.uk ...

On 2003年8月5日星期二23:58:39 -0500,Dave Rahardja< as*@me.com>写道:

class baz:public foo
{
public:
void bar(){frobnicate(); do_bar(); } //我需要一些不同的东西!
proctected:
virtual void bar(){} //覆盖
};



当然: {
proctected:
virtual void do_bar(){
frobnicate();
//更多东西
}
};




除非派生自此类,否则可能不会调用此do_bar()

首先。


DW


我想这是证明你不应该这样做的消息之一当你整天测试代码时写一个

哲学信息;-)

class foo
{
公开:
void bar(){do_bar(); }
protected:
virtual void bar(){}
你的意思不是:virtual void do_bar(){}?




是。

proctected:
virtual void bar(){} //被覆盖



virtual void do_bar(){}再次?




再次是。


另一个是没有它你的公众界面混杂着每个派生类中相同的功能。我希望尽可能保持最小的公共界面。如果一个成员在基类中是公共的,那么它不需要在任何其他地方公开。所以我使用它即使我无法想象
做更多的事情而不是从公共非虚拟中调用受保护的虚拟。




我恐怕我看不出模板方法模式如何减少公共界面中的混乱局面:你有一个公共的非虚方法,每一个

受保护虚方法。与公共非虚基类

方法的情况一样,您只需在以下情况下覆盖公共虚拟基类方法:


class dee br />
{

public:

virtual void dum(){}

};


class duh

{

public:

//使用dum的基类实现()

};


如果我们使用模板方法,那么在公共界面中,类没有任何额外的混乱,而不是

。 />

在上面的类层次结构中,两个调用......

baz baz_object;
foo& ; foo_reference(baz_object);
baz& baz_reference(baz_object);

foo.bar();
baz.bar();

...将产生两个不同的调用路径。


是的,这就是我不喜欢的原因。




另一个错误;这些电话应该阅读


foo_reference.bar();

baz_reference.bar();


我是什么试图说明是因为baz的作者试图通过重新定义条形图来实现foo :: bar()(而不是foo :: do_bar())
方法。也许foo :: bar()操纵了一个信号量,但是baz的作者确实没有想要这样做。另外,她还希望所有派生类baz

使用baz :: bar()方法而不是foo :: bar()。

他们是否将一类错误换成另一类错误?



不,除了使用它之外你还能获得什么?不得不写一个少于
的功能?公共非虚拟可以是内联的,因此甚至没有性能成本。




虽然这些额外的方法往往很短,内联的方法,它们会增加额外的代码,这会增加错误的可能性,但那不是我最关心的错误类型关于。


当作者试图通过大量派生类使用的算法修复

问题时,会发生最隐蔽的错误。修复

公共非虚基类方法。即使修复程序看起来有效,他仍然需要检查并重新测试所有派生类,以确保

他没有破坏任何东西。 br />

这是一个例子:


class foo

{

public :

void dee(){pre_dee(); do_dee(); post_dee(); }

void dum(){pre_dum(); do_dum(); post_dum(); }

受保护:

虚拟空虚do_dee(){}

虚拟空虚do_dum(){}

} ;


班级栏:public foo

{

proctected:

virtual void do_dee() {dum(); frobnicate(); } //注意调用dum()

};


我们可以假设bar :: do_dee()调用foo :: dum()因为以前的方法

需要在foo:dum()中找到的前后代码。


然后作者发现他需要序列化访问foo

层次结构通过互斥对象保护其公共方法,所以

他将foo方法更改为:


class foo

{

public:

void dee(){acquire(); pre_dee(); do_dee(); post_dee(); relinquish()}

void dum(){acquire(); pre_dum(); do_dum(); post_dum();放弃()}

受保护:

虚拟空虚do_dee(){}

虚拟空虚do_dum(){}

};


但是请注意他现在已经破坏了bar :: do_dee(),因为acquire()现在将连续两次调用
可能导致死锁。


我想我们可以施加约束,即派生类只调用

受保护的do_xxx方法,而不是它们的公共非虚拟对应方式。

但是,在publc

非虚基类方法中收集常见的前后代码的用处将丢失。

为了最后说明为什么疲惫的大脑无法在不引入错误的情况下表达想法,我给你了......

模板方法模式与Interface方法不兼容,



我也必须看一下。

not
因为它无法完成,但因为组合它们没有意义。


非虚拟公共方法工作就是简单地称为纯虚拟的受保护方法 - 这只是愚蠢的。


该段应为:


模板方法模式与接口方法不兼容,不是

因为它无法完成,但因为将它们组合起来毫无意义。空的

非虚拟公共方法,它的工作就是简单地调用纯虚拟的

PUBLIC方法 - 这只是愚蠢。
$ b $那么它就是其中之一。什么是更好的设计范例?



两者。




很好。


OK, so I''ve gotten into a philosophical disagreement with my colleague at
work. He is a proponent of the Template Method pattern, i.e.:

class foo
{
public:
void bar() { do_bar(); }
protected:
virtual void do_bar() {}
};

class baz: public foo
{
protected:
virtual void do_bar() {} // overridden
};

As you can see, the non-virtual public method foo:bar() exists only to invoke
the virtual method do_bar, which derivatives of the class can override like a
regular virtual method.

The advantage to this method, my colleague claims, is that future implementors
may change the pre- and post-processing behavior of the virtual do_bar method
(such as adding semaphore acquisition/relinquishment calls before/after the
call to do_bar, respectively) without changing the classes that derive from
foo.

Furthermore, he claims that this separates the decision to make a method
public/private from the decision to make a method virtual--a significant
advantage, he claims.

He has pointed me to articles on C++ Users Journal which seems to support the
Template Method pattern, even going so far as to say that all public virtual
methods should be banned in favor of the Template Method.

See http://www.cuj.com/documents/s=8000/cujcexp1812hyslop/

I, on the other hand, prefer the simple

class foo
{
public:
virtual void bar() {}
};

class baz: public foo
{
public:
virtual void bar() {} // overridden
};

And being a big fan of Java, I also appreciate the Interface pattern:

class foo // interface
{
public:
virtual void bar() = 0;
};

class baz: virtual public foo
{
public:
virtual void bar() {} // implementation
};

Besides, the Template Method can be ruined if a derived class decides that it
needs a different pre- and post-operation for the virtual method...

class foo
{
public:
void bar() { do_bar(); }
protected:
virtual void bar() {}
};

class baz: public foo
{
public:
void bar() { frobnicate(); do_bar(); } // I need something different!
proctected:
virtual void bar() {} // overridden
};

In the above class hierarchy, the two calls...

baz baz_object;
foo& foo_reference(baz_object);
baz& baz_reference(baz_object);

foo.bar();
baz.bar();

....would yield two different call paths.

Of course you can say "don''t do that", but I can also say "but I need to
override the default behavior for this class".

So what say you, gentle coders? Are Template Methods worth the hassle? Do they
trade one class of errors for another? Is the convenience of changing pre- and
post-operations at the base class actually masking the fact that we have made
a design error and honestly need to re-examine every descendant class?

The Template Method pattern is not compatible with the Interface method, not
because it can''t be done, but because it makes no sense to combine them. Empty
non-virtual public methods whose jobs it is is to simply call pure virtual
protected methods--that''s just silly.

So it''s one or the other, then. What''s the better design paradigm?

解决方案

"Dave Rahardja" <as*@me.com> wrote in message
news:2r********************************@4ax.com...

OK, so I''ve gotten into a philosophical disagreement with my colleague at
work. He is a proponent of the Template Method pattern, i.e.:

class foo
{
public:
void bar() { do_bar(); }
protected:
virtual void do_bar() {}
};

class baz: public foo
{
protected:
virtual void do_bar() {} // overridden
};

As you can see, the non-virtual public method foo:bar() exists only to invoke the virtual method do_bar, which derivatives of the class can override like a regular virtual method.
I use this for large hierarchies, though I didn''t know it had a name (I do
have Design Patterns; guess I''ll have to refresh my memory).
The advantage to this method, my colleague claims, is that future implementors may change the pre- and post-processing behavior of the virtual do_bar method (such as adding semaphore acquisition/relinquishment calls before/after the call to do_bar, respectively) without changing the classes that derive from foo.
Yes, that''s one reason.

Another is that without it your public interface is cluttered up with the
same functions in every derived class. I like to keep the smallest public
interface possible. If a member is public in the base class it doesn''t need
to be public anywhere else. So I use it even if I can''t imagine ever wanting
to do more than call the protected virtual from the public non-virtual.

Another reason is that you can add a breakpoint or some debugging code to
the public function; all calls have to go through there.

I have classes in which I use this pattern for all these reasons.
Furthermore, he claims that this separates the decision to make a method
public/private from the decision to make a method virtual--a significant
advantage, he claims.
I don''t see much in that reason. I wouldn''t have a function''s virtualness
influencing its access rights (in those smaller hierarchies where I don''t
bother with this method).
He has pointed me to articles on C++ Users Journal which seems to support the Template Method pattern, even going so far as to say that all public virtual methods should be banned in favor of the Template Method.
No need to ban it. As much as I like the Template Method, I don''t think
public virtuals are actually evil.
See http://www.cuj.com/documents/s=8000/cujcexp1812hyslop/

I, on the other hand, prefer the simple

class foo
{
public:
virtual void bar() {}
};

class baz: public foo
{
public:
virtual void bar() {} // overridden
};

And being a big fan of Java, I also appreciate the Interface pattern:

class foo // interface
{
public:
virtual void bar() = 0;
};

class baz: virtual public foo
{
public:
virtual void bar() {} // implementation
};

Besides, the Template Method can be ruined if a derived class decides that it needs a different pre- and post-operation for the virtual method...

class foo
{
public:
void bar() { do_bar(); }
protected:
virtual void bar() {}
Don''t you mean: virtual void do_bar() {}?
};

class baz: public foo
{
public:
void bar() { frobnicate(); do_bar(); } // I need something different!
If this is what''s suitable for the class. Are you sure that this function
should be called bar() though? Maybe a different name would be more
suitable. If it should be bar() then I''d suggest making it virtual as well
(and protected everywhere, and add a new public function Bar() to the base
class that calls bar(); i.e., a double template method!).
proctected:
virtual void bar() {} // overridden
virtual void do_bar() {} again?
};

In the above class hierarchy, the two calls...

baz baz_object;
foo& foo_reference(baz_object);
baz& baz_reference(baz_object);

foo.bar();
baz.bar();

...would yield two different call paths.
Yes, that''s why I don''t like it.
Of course you can say "don''t do that", but I can also say "but I need to
override the default behavior for this class".
You haven''t explained how _not_ using the template method solves your
problem.
So what say you, gentle coders? Are Template Methods worth the hassle?
For large hierarchies with a large number of what would have been public
virtual functions, I think so.
Do they
trade one class of errors for another?
No, what do you gain by not using it, apart from having to write one less
function? The public non-virtual can be inline, so there isn''t even a
performance cost.
Is the convenience of changing pre- and
post-operations at the base class actually masking the fact that we have made a design error and honestly need to re-examine every descendant class?
You can''t possibly generalize about that. It depends on the particular
circumstances. I see nothing inherently wrong with having pre- and post-
operations, if that''s the reason for using the method.
The Template Method pattern is not compatible with the Interface method,
I''ll have to look that one up as well.
not
because it can''t be done, but because it makes no sense to combine them. Empty non-virtual public methods whose jobs it is is to simply call pure virtual
protected methods--that''s just silly.
That''s exactly what I do, and I have a nice small public interface
throughout the hierarchy with no performance cost.
So it''s one or the other, then. What''s the better design paradigm?



Both.

DW


"tom_usenet" <to********@hotmail.com> wrote in message
news:3f****************@news.easynet.co.uk...

On Tue, 05 Aug 2003 23:58:39 -0500, Dave Rahardja <as*@me.com> wrote:

class baz: public foo
{
public:
void bar() { frobnicate(); do_bar(); } // I need something different!
proctected:
virtual void bar() {} // overridden
};



Surely:

class baz: public foo
{
proctected:
virtual void do_bar() {
frobnicate();
//more stuff
}
};



Except that if this class is derived from, this do_bar() might not be called
first.

DW


I suppose this is one of those messages that prove that you shouldn''t write a
philosophical message when you''ve been testing code all day ;-)

class foo
{
public:
void bar() { do_bar(); }
protected:
virtual void bar() {}
Don''t you mean: virtual void do_bar() {}?



Yes.

proctected:
virtual void bar() {} // overridden



virtual void do_bar() {} again?



Yes again.

Another is that without it your public interface is cluttered up with the
same functions in every derived class. I like to keep the smallest public
interface possible. If a member is public in the base class it doesn''t need
to be public anywhere else. So I use it even if I can''t imagine ever wanting
to do more than call the protected virtual from the public non-virtual.



I''m afraid I don''t see how the Template Method pattern reduces the clutter in
the public interface: you have one public non-virtual method for every
protected virtual method. As is the case with public non-virtual base class
methods, you only override public virtual base class methods when you need to:

class dee
{
public:
virtual void dum() {}
};

class duh
{
public:
// Use base class implementation of dum()
};

The class duh doesn''t have any additional clutter in its public interface than
if we were to use the Template Method.

In the above class hierarchy, the two calls...

baz baz_object;
foo& foo_reference(baz_object);
baz& baz_reference(baz_object);

foo.bar();
baz.bar();

...would yield two different call paths.



Yes, that''s why I don''t like it.



Another mistake; those calls should read

foo_reference.bar();
baz_reference.bar();

What I was trying to illustrate was that the author of baz was trying to
preempt what foo::bar() (not foo::do_bar()) was doing by redefining the bar()
method. Maybe foo::bar() manipulated a semaphore, but the author of baz did
not want to do that. Additionally, she also wanted all derived classes of baz
to use the baz::bar() method instead of foo::bar().

Do they
trade one class of errors for another?



No, what do you gain by not using it, apart from having to write one less
function? The public non-virtual can be inline, so there isn''t even a
performance cost.



Although those extra methods tend to be very short, inlined methods, they do
add up to additional code, which increases the potential of errors, but that''s
not the kind of errors I''m most concerned about.

The errors that are most insidious take place when an author tries to fix a
problem in an algorithm used by a large number of derived classes by fixing
the public non-virtual base class method. Even if the fix appears to work, he
still needs to examine and re-test all of the derived classes to ensure that
he hasn''t broken anything.

Here''s an example:

class foo
{
public:
void dee() { pre_dee(); do_dee(); post_dee(); }
void dum() { pre_dum(); do_dum(); post_dum(); }
protected:
virtual void do_dee() {}
virtual void do_dum() {}
};

class bar: public foo
{
proctected:
virtual void do_dee() { dum(); frobnicate(); } // note call to dum()
};

We can assume that bar::do_dee() calls foo::dum() because the former method
needs the pre- and post-code found in foo:dum().

The author then discovers that he needs to serialize access to the foo
hierarchy by protecting its public methods with a mutual exclusion object, so
he changes the foo methods to read:

class foo
{
public:
void dee() { acquire(); pre_dee(); do_dee(); post_dee(); relinquish() }
void dum() { acquire(); pre_dum(); do_dum(); post_dum(); relinquish() }
protected:
virtual void do_dee() {}
virtual void do_dum() {}
};

But note that he has now broken bar::do_dee(), because acquire() will now be
called twice in a row, possibly resulting in deadlock.

I guess we can impose the constraint that derived classes only call the
protected do_xxx methods instead of their public non-virtual counterparts.
However, the usefulness of gathering common pre- and post-codes in the publc
non-virtual base class methods is lost.
And for our final illustration on why a tired brain cannot express ideas in a
Usenet message without introducing errors I give you...

The Template Method pattern is not compatible with the Interface method,



I''ll have to look that one up as well.

not
because it can''t be done, but because it makes no sense to combine them.


Empty

non-virtual public methods whose jobs it is is to simply call pure virtual
protected methods--that''s just silly.
That paragraph should read:

The Template Method pattern is not compatible with the Interface method, not
because it can''t be done, but because it makes no sense to combine them. Empty
non-virtual public methods whose jobs it is is to simply call pure virtual
PUBLIC methods--that''s just silly.
So it''s one or the other, then. What''s the better design paradigm?



Both.



Nice.


这篇关于哲学问题:模板方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆