﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-雪梨酒-文章分类-设计模式</title><link>http://www.cppblog.com/sherry/category/5927.html</link><description>---cpp</description><language>zh-cn</language><lastBuildDate>Fri, 23 May 2008 08:31:07 GMT</lastBuildDate><pubDate>Fri, 23 May 2008 08:31:07 GMT</pubDate><ttl>60</ttl><item><title>Pattern Designer ---- Template </title><link>http://www.cppblog.com/sherry/articles/40396.html</link><dc:creator>sherry</dc:creator><author>sherry</author><pubDate>Fri, 04 Jan 2008 05:56:00 GMT</pubDate><guid>http://www.cppblog.com/sherry/articles/40396.html</guid><wfw:comment>http://www.cppblog.com/sherry/comments/40396.html</wfw:comment><comments>http://www.cppblog.com/sherry/articles/40396.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sherry/comments/commentRss/40396.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sherry/services/trackbacks/40396.html</trackback:ping><description><![CDATA[<p>Template 模式<br>&nbsp;&nbsp;&nbsp;&nbsp;一看，Template 模式觉得会不会跟类模板，函数模板一样觉得有些无所适从。<br>&nbsp;&nbsp;&nbsp; 往下看才知道，这个东西相当的easy。说它easy是因为说来说去就是一个继承。<br>&nbsp;&nbsp;&nbsp;&nbsp;使用它来设计类的步骤就是：定义一个虚类，在里面有自己实现N多东西的过程，可是当你想要把这个类应用到N多场合（e.g&nbsp; 你想使用一把枪杀人 ，前面这个动作可以作为一个这个虚类的一个动作，当然，你可以使用小白，沙鹰，AK ，43等。）。就是说你不管使用什么东西，就是杀人就OK了，那么就总不能把所有的情况列举在这个类中吧，晕，即使可以，你使用也不爽啊。<br>所以，可以把这些动作（算法）放到继承它的子类中。就是，继承该接口。<br>&nbsp;&nbsp;&nbsp;与stratgy(策略)模式的差别就是，使用的是继承跟组合实现的差别。<br><br><br>&nbsp;&nbsp;&nbsp;-----------------------------以下文字是引用的一个更深刻的Template的使用---------------<br></p>
<p>----------------------------------------------------------</p>
<p>人物介绍：</p>
<p>我 --- 一个追求上进的C++程序员，尚在试用期，聪明但是经验不足。</p>
<p>Wendy --- 公司里的技术大拿，就坐在我旁边的隔间里，C++大虾，最了不起的是，她是个女的 她什麽都好，就是有点刻薄，</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我对她真是又崇拜又嫉妒。</p>
<p>----------------------------------------------------------</p>
<p>I. Virtually Yours?-- Template Method模式</p>
<p>我在研究Wendy写的一个类。那是她为这个项目写的一个抽象基类，而我的工作就是从中派生出一个具象类(concrete class)。这个类的public部份是这样的：</p>
<pre>class Mountie {
public:
void read( std::istream &amp; );
void write( std::ostream &amp; ) const;
virtual ~Mountie();</pre>
<p>很正常，virtual destructor表明这个类打算被继承。那麽再看看其protected部份：</p>
<pre>protected:
virtual void do_read( std::istream &amp; );
virtual void do_write( std::ostream &amp; ) const;
</pre>
<p>也不过就是一会儿的功夫，我识破了Wendy的把戏：她在使用template method模式。public成员函数read和write是非虚拟的，它们肯定是调用protected部份do_read/do_write虚拟成员函数来完成实际的工作。啊，我简直为自己的进步而飘飘然了 哈，Wendy，这回你可难不住我，还有什麽招数？尽管放马过来... 突然，笑容在我脸上凝固，因为我看到了其private部份：</p>
<pre>private:
virtual std::string classID() const = 0;
</pre>
<p>这是什麽？一个private纯<font color=#ff00ff>序</font>函数，能工作麽？我站了起来， </p>
<p>&#8220;Wendy，你的Mountie类好像不能工作耶，它有一个private virtual function。&#8221;</p>
<p>&#8220;你试过了？&#8221;她连头都不抬。</p>
<p>&#8220;嗯，那倒是没有啦，可是想想也不行啊？我的派生类怎麽能override你的private函数呢？&#8221; 我嘟囔 。</p>
<p>&#8220; ，你倒是很确定啊 &#8221;Wendy的声音很轻柔，&#8220;你怎麽老是这也不行，那也不行的，这几个月跟 我你就没学到什麽东西吗？小菜鸟。&#8221;</p>
<p>真是可恶啊...</p>
<p>&#8220;小菜鸟，你全都忘了，<font color=#ff00ff>访问控制级别跟一个函数是不是虚拟的根本没关系</font>。<font color=#ff00ff>判断一个函数是动态绑定还是静态绑定是函数调用解析的最後一个步骤</font>。好好读读<font color=#ff00ff>标准</font>的3.4和5.2.2节吧。&#8221;</p>
<p>我完全处於下风，只好采取干扰战术。&#8220;好吧，就算你说的不错，我也还是不明白，何必把它设为private？&#8221;</p>
<p>&#8220;我且问你，倘若你不想让一个类中的成员函数被其他的类调用，应当如何处理？&#8221;</p>
<p>&#8220;当然是把它设置为private<font color=#ff00ff>的</font>，&#8221; 我回答道。</p>
<p>&#8220;那麽你去看看我的<font color=#ff00ff>Mountie类实现</font>，特别是write()函数的实现。&#8221;</p>
<p>我正巴不得逃开Wendy那刺人的目光，便转过头去在我的屏幕上搜索，很快，我找到了：</p>
<pre>void Mountie::write(std::ostream &amp;Dudley) const
{
Dudley &lt;&lt; classID() &lt;&lt; std::endl;
do_write(Dudley);
}</pre>
<p>嗨，最近卡通片真是看得太多了，居然犯这样的低级失误。还是<font color=#ff00ff>老是</font>承认吧：&#8220;好了，我明白了。classID()是一个实现细节，用来在保存对象时指示具象类的类型，派生类必须覆盖它，所以必须是纯虚的。但是既然是实现细节，就应该设为private的。&#8221;</p>
<p>&#8220;这还差不多，小菜鸟。&#8221;大虾点了点头，&#8220;现在给我解释一下为什麽do_read()和do_write()是protected的？&#8221;</p>
<p>这个问题并不难，我组织了一下就回答：&#8220;因为派生类对象需要调用这两个函数的实现来读写其中的基类对象。&#8221;</p>
<p>&#8220;很好很好，&#8221;大虾差不多满意了，&#8220;不过，你再解释解释为什麽我不把它们设为public的？&#8221;</p>
<p>现在我感觉好多了：&#8220;因为调用它们的时候必须以一种特定的方式进行。比如do_write()函数，必须先把类型信息写入，再把对象信息写入，这样读取的时候，负责生成对象的模块首先能够知道要读出来的对象是什麽类型的，然後才能正确地从流中读取对象信息。&#8221;</p>
<p>&#8220;聪明啊，我的小菜鸟 &#8221;Wendy停顿了一下，&#8220;就跟学习外国口语一样，<font color=#8080ff>学习C++也不光是掌握语法而已，还必须要掌握大量的惯用法</font>。&#8221;</p>
<p>&#8220;是啊是啊，我正打算读Coplien的书...&#8221;</p>
<p>[<font color=#ff0000>译者注</font>：就是James Coplien 1992年的经典着作Advanced C++ Programming Style and Idioms]</p>
<p>大虾挥了挥她的手，&#8220;冷静，小菜鸟，我不是指<font color=#8080ff>先知Coplien</font>的那本书，我是指某种结构背後隐含的惯用法。比如<font color=#8080ff>一个类有virtual destructor，相当于告诉你说：&#8216;嗨，我是一个多态基类，来继承我吧 ＇ 而如果一个类的destructor不是虚拟的，则相当於是在说：&#8216;我不能作为多态基类，看在老天的份上，别继承我。</font>＇&#8221;</p>
<p>&#8220;同样的，<font color=#8080ff>virtual函数的访问控制级别也具有隐含的意义。一个protected virtual function告诉你：&#8216;你写的派生类应该，哦，可是说是必须调用我的实现。＇而一个private virtual function是在说：&#8216;派生类可以覆盖，也可以不覆盖我，随你的便。但是你不可以调用我的实现</font>。＇&#8221;</p>
<p>我点点头，告诉她我懂了，然後追问道：&#8220;那麽public virtual function呢？&#8221;</p>
<p>&#8220;<font color=#8080ff>尽可能不要使用public virtual function</font>。&#8221;她拿起一支笔写下了以下代码：</p>
<pre style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">class HardToExtend </pre>
<pre style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">{</pre>
<pre style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">public:</pre>
<pre style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">	 virtual void f();</pre>
<pre style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">};</pre>
<pre style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%"> void HardToExtend::f() </pre>
<pre style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">{ </pre>
<pre style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">	// Perform a specific action </pre>
<pre style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">}</pre>
<p style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%"></p>
<p>&#8220;假设你发布了这个类。在写第二版时，需求有所变化，你必须改用Template Method。可是这根本不可能，你知道为什麽？&#8221;</p>
<p>&#8220;呃，这个...，不知道。&#8221;</p>
<p>&#8220;<font color=#ff00ff>由</font>两种可能的办法。其一，将f()的实现代码转移到一个新的函数中，然後将f()本身设为non-virtual<font color=#ff00ff>的</font>：</p>
<pre>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
}
</pre>
<p>然而你原来写的派生类都是企图override函数f()而不是do_f()<font color=#ff00ff>的</font>，你必须改变所有的<font color=#ff00ff>派生类实现</font>，只要你错过了一个类，你的类层次就会染上<font color=#8080ff>先知Meyers</font>所说的&#8216;精神分裂的行径＇。&#8221;[<font color=#ff0000>译者注</font>：叁见Scott Meyers，Effective C++, Item 37，绝对不要重新定义继承而来的非虚拟函数]</p>
<p>&#8220;另一种办法是将f()移到private区域，引入一个新的non-virtual函数：&#8221;</p>
<pre>class HardToExtend
{
// possibly protected
virtual void f();
public:
void call_f();
};</pre>
<p>&#8220;这会导致无数令人头痛的问题。首先，所有的客户都企图调用f()而不是call_f()，现在它们的代码都不能编译了。更有甚者，大部份派生类都<font color=#ff00ff>回</font>把f()放在public区域中，这样直接使用派生类的用户可以访问到你本来想保护的细节。&#8221;</p>
<p>&#8220;对待虚函数要<font color=#ff00ff>象</font>对待数据成员一样，把它们设为private<font color=#ff00ff>的</font>，直到设计上要求使用更宽松的访问控制再来调整。要知道<font color=#8080ff>由private入public易，由public入private难啊</font> &#8221;</p>
<p>[<font color=#ff0000>译者注</font>：这篇文章所表达的思想具有一定的颠覆性，因为我们太容易在基类中设置public virtual function了，Java中甚至专门为这种做法建立了interface机制，现在竟然说这不好 一时间真是接受不了。但是仔细体会作者的意思，他并不是<font color=#ff00ff>一般地</font>反对public virtual function，只是在template method大背景下给出上述原则。虽然这个原则在一般的设计中也是值得考虑的，但是主要的应用领域还是在template method模式中。当然，template method是一种非常有用和常用的模式，因此也决定了本文提出的原则具有广泛的意义。]</p>
<p>----------------------------------------------------------------</p>
<img src ="http://www.cppblog.com/sherry/aggbug/40396.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sherry/" target="_blank">sherry</a> 2008-01-04 13:56 <a href="http://www.cppblog.com/sherry/articles/40396.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>