<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type='text/xsl' href='http://aspnet.spaces.live.com/mmm2008-07-24_12.50/rsspretty.aspx?rssquery=en-US;http%3a%2f%2faspnet.spaces.live.com%2fcategory%2fDesign%2b__x7%2bPattern%2ffeed.rss' version='1.0'?><rss version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:msn="http://schemas.microsoft.com/msn/spaces/2005/rss" xmlns:live="http://schemas.microsoft.com/live/spaces/2006/rss" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:cf="http://www.microsoft.com/schemas/rss/core/2005" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Microsoft.NET BLOG(YunYang): Design &amp; Pattern</title><description /><link>http://aspnet.spaces.live.com/?_c11_BlogPart_BlogPart=blogview&amp;_c=BlogPart&amp;partqs=catDesign%2b__x7%2bPattern</link><language>en-US</language><pubDate>Thu, 04 Sep 2008 09:37:07 GMT</pubDate><lastBuildDate>Thu, 04 Sep 2008 09:37:07 GMT</lastBuildDate><generator>Microsoft Spaces v1.1</generator><docs>http://www.rssboard.org/rss-specification</docs><ttl>60</ttl><cf:parentRSS>http://aspnet.spaces.live.com/blog/feed.rss</cf:parentRSS><live:type>blogcategory</live:type><live:identity><live:id>-5487770763778616419</live:id><live:alias>aspnet</live:alias></live:identity><cf:listinfo><cf:group ns="http://schemas.microsoft.com/live/spaces/2006/rss" element="typelabel" label="Type" /><cf:group ns="http://schemas.microsoft.com/live/spaces/2006/rss" element="tag" label="Tag" /><cf:group element="category" label="Category" /><cf:sort element="pubDate" label="Date" data-type="date" default="true" /><cf:sort element="title" label="Title" data-type="string" /><cf:sort ns="http://purl.org/rss/1.0/modules/slash/" element="comments" label="Comments" data-type="number" /></cf:listinfo><item><title>从容自若的CTO</title><link>http://aspnet.spaces.live.com/Blog/cns!B3D78590D833939D!327.entry</link><description>&lt;div&gt;
&lt;h3&gt;bruce zhang关于Design &amp;amp; Pattern的文章挺不错,今天看到从容自若的CTO感觉不错,转一个.&lt;/h3&gt;
&lt;div&gt;让我们假设这样一个场景：一年以前，Media公司开发出一套通过电脑接收广播的Radio仿真软件产品。（有这样的产品吗，能真正接收广播的软件？我表示怀疑）这个产品早已投入市场，客户已经在使用了。后来，Media公司将开发重心转移到数字媒体上。于是他们投入了大量的人力物力，最后开发出了完美的媒体播放器软件。这个播放器支持大多数媒体文件，包括音频媒体和视频媒体。该产品取得了成功，也得到了用户的好评。&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;不过，现实生活中总有些刁钻的客户，比如说wayfarer，就是鄙人了，素爱怀旧。在使用媒体播放器的时候，想起了在初中的时候就使用的收录机。磁带、广播，一机两用，真是令人怀念。于是我向Media公司提出了建议，希望能在媒体播放器中增加收音的功能。Media的CEO对这个似乎有些嗤之以鼻。可是像wayfarer这样的用户越来越多，呼声也越来越高。为了产品的市场，为了公司的前景，这位CEO不得不慎重考虑这个需求了。当首席执行官就是好，赶紧把这个烫手山芋抛给了CTO。
&lt;p&gt;却看这位CTO仍然是从容不迫，脸上挂满自信的微笑。CEO不解，问他何故如此从容？CTO淡然一笑，吐出一字真言：“Adapter”。
&lt;p&gt;呵呵，笑话了。设计模式可不是什么Bible，也非神奇的魔咒。不过对于以上场景，使用Adapter却是最佳的应用！且请听我慢慢道来。
&lt;p&gt;已有产品：MediaPlayer、RadioPlayer；&lt;br&gt;分析：MediaPlayer是面向客户的外观，即表示层，它调用了对应的业务层，该层实现了IMedia接口。同理RadioPlayer也是面向客户的外观，它调用的业务层，是收听广播的业务，并实现了IRadio接口。&lt;br&gt;目的：将RadioPlayer的业务添加到MediaPlayer的外观中。原有的RadioPlayer不再使用。
&lt;p&gt;既然与MediaPlayer、RadioPlayer的业务有关，所以我们有必要分析其各自的业务结构。MediaPlayer业务层结构：
&lt;p align=center&gt;&lt;img height=434 src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/wing10.gif" width=425 border=0&gt;
&lt;p&gt;为了简化，我这里将所有的方法都放在一个接口IMedia里（这个设计还有很多重构的空间，我会在后续文章中继续关注）。在本文的结构中，视频媒体和音频媒体的方法是相同的，本来我可以令各媒体文件继承同一个抽象类Media。但现实情况显然不是这样，所以我仍然保留这个系列文章中原有的结构。以下是每个方法的说明：&lt;br&gt;    Play()：播放媒体文件；&lt;br&gt;    Stop()：停止播放；&lt;br&gt;    Pause()：暂停播放；&lt;br&gt;    OpenFile()：打开媒体文件；&lt;br&gt;    CloseFile()：关闭媒体文件；&lt;br&gt;    Forward()：前进播放文件；&lt;br&gt;    Back()：后退播放文件；
&lt;p&gt;OK，我们再来看看RadioPlayer的业务层结构：
&lt;p align=center&gt;&lt;img height=243 src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/wing11.gif" width=462 border=0&gt;&lt;br&gt;RadioPlayer的业务均抽象为IRadio接口。并由抽象类Radio实现该接口。FM为调频收音，SW为短波收音。另外还有其他的，例如中波等，就不在详细列出。各方法的功能说明如下：&lt;br&gt;    Receive()：接收广播；&lt;br&gt;    Stop()：停止接收广播；&lt;br&gt;    TurnOn()：打开收音；&lt;br&gt;    TurnOff()：关闭收音；&lt;br&gt;    ChangeChannel(bool direction)：切换频率。参数direction为true时，则往上；否则往下。当然也可以使用枚举类型。
&lt;p&gt;媒体播放器的业务由一个统一的Client类进行处理，它包括一系列的静态方法以实现对原有媒体类型的调用：&lt;br&gt;public class Client&lt;br&gt;{&lt;br&gt; public static void Play(IMedia media)&lt;br&gt; {&lt;br&gt;  media.Play();&lt;br&gt; }
&lt;p&gt; public static void OpenFile(IMedia media)&lt;br&gt; {&lt;br&gt;  media.OpenFile();&lt;br&gt; }&lt;br&gt; &lt;br&gt; //……其他方法略；&lt;br&gt;}&lt;br&gt;MediaPlayer播放器本身，其外观则是一个WinForm应用程序，该应用程序将调用Client的相关静态方法。如：&lt;br&gt;Client.Play(new MP3());
&lt;p&gt;现在看看我们需要实现的。我需要将RadioPlayer的业务，即抽象为IRadio接口的对象，放到MediaPlayer中。糟糕的是，Client的各个方法传递的参数类型，为IMedia接口。怎么才能将实现IRadio接口的对象传递到Client的方法中呢？对了，这就是适配，就是为IRadio对象适配成符合IMedia接口行为的过程。打一个不好听的比方，就好比一只狼，要让自己钻进羊群里，而不被发现，就需要找一张羊皮来披上。俗语云：“披着羊皮的狼”是也。不过，我们要注意的是，狼虽然不是羊，但有着和羊相似的属性。它和羊体形相似，照样能跑，能吃，只是吃的不是草，而是肉而已。你总不能为一张桌子披上羊皮，去装羊吧。而文中的IMedia类型和IRadio类型，还是有很多相似之处的。
&lt;p&gt;现在，我们就为IRadio接口进行适当的包装。由于这是两个接口进行匹配的过程，所以我们通常名之为“适配”，而非“包裹”。那么它们之间有相似性吗？有！&lt;br&gt;IMedia    IRadio&lt;br&gt;Play()    Receive()&lt;br&gt;Stop()    Stop()&lt;br&gt;OpenFile()   TurnOn()&lt;br&gt;CloseFile()   TurnOff()&lt;br&gt;Forward()   ChangeChannel(true)&lt;br&gt;Back()    ChangeChannel(false)
&lt;p&gt;当然现实情况并非总是那么完美。可能IMedia的方法中，IRadio可能并不需要。没关系，我们只提供该方法就可以了，方法的实现可以为空，如Pause()方法。也有可能IRadio的一些方法，IMedia并没有，此时的Adaptor模式，就将被适配对象的接口变宽了，也就是说引入了新的行为，这就类似于我系列文章之二所描述的。
&lt;p&gt;不管现实的某些情况是多么的不如意，但至少通过引入Adapter模式，我们就不需要改变原有的IMedia和IRadio的相关对象与业务了。要修改的，仅仅是客户端，以及增加一个新的Adapter结构而已。
&lt;p&gt;分析结束，开始动手术吧。先看类的Adapter模式： 
&lt;p align=center&gt;&lt;img height=556 src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/wing12.gif" width=554 border=0&gt;&lt;br&gt;类图好像很复杂，不过请大家主要关注橙色的两个类FMAdapter和SWAdapter。FMAdapter类是FM类型的Adapter，它继承了FM类，并实现了IMedia接口。通过这种方式，原有的FM类型的行为，就被适配为符合IMedia类型的新类型。代码如下：&lt;br&gt;public class FMAdapter:FM,IMedia&lt;br&gt;{&lt;br&gt; public void Play()&lt;br&gt; {&lt;br&gt;  this.Receive();&lt;br&gt; }&lt;br&gt; public void Forward()&lt;br&gt; {&lt;br&gt;  this.ChangeChannel(true);&lt;br&gt; }&lt;br&gt; public void Pause(){}//Radio类型没有该行为，令其为空，或引入异常机制；&lt;br&gt; //其他方法略&lt;br&gt; …… &lt;br&gt;}&lt;br&gt;SWAdapter的实现方式完全相同，就不赘述。
&lt;p&gt;由于新的Adapter类均实现了IMedia接口，因此，该类型的对象可以安全正确地作为Client静态方法的参数对象传入。从外部行为的表现来看，没有区别。如：&lt;br&gt;Client.Play(new FMAdapter());&lt;br&gt;它调用了FMAdapter的Play方法，而其内部，实质上调用的是FM的Receiver()方法。
&lt;p&gt;再看对象的Adapter模式，就更简单了。 
&lt;p align=center&gt;&lt;img height=403 src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/wing13.gif" width=554 border=0&gt;
&lt;p&gt;只需要一个Adapter类RadioAdapter，然后实现IMedia接口。没有继承关系了，而是聚合了Radio对象。注意，这里聚合的是抽象类对象Radio，而不是具体的FM或SW。&lt;br&gt;public class RadioAdapter:IMedia&lt;br&gt;{&lt;br&gt; private Radio _radio; &lt;br&gt; public RadioAdapter(Radio radio)&lt;br&gt; {&lt;br&gt;  this._radio = radio;&lt;br&gt; }&lt;br&gt; public void Play()&lt;br&gt; {&lt;br&gt;  _radio.Receive();&lt;br&gt; }&lt;br&gt; public void Forward()&lt;br&gt; {&lt;br&gt;  _radio.ChangeChannel(true);&lt;br&gt; }&lt;br&gt; public void Pause(){}//Radio类型没有该行为，另其为空，或引入异常机制；&lt;br&gt; //其他方法略&lt;br&gt; …… &lt;br&gt;}&lt;br&gt;调用Client的静态方法：&lt;br&gt;Client.Play(new RadioAdapter(new FM()));
&lt;p&gt;通过引入Adapter模式，我们在不改变原有IMedia和IRadio的情况下，顺利地将IRadio类型适配成了IMedia类型。此时，我们只需要在MediaPlayer的客户端调用中加入原来RadioPlayer的业务即可，基本保证了原有系统的稳定性。
&lt;p&gt;上述实例，才真正体现了Adapter的价值（请大家一定注意区分本文实例需求，与系列之二实例需求的区别）。因此，我们可以得到两个结论：&lt;br&gt;1、通过Adapter模式，为适配对象引入以前不具备的行为；此时建议使用类的Adapter模式。理由请参考：系列文章之二与之三；&lt;br&gt;2、将一个固有对象适配为另一种接口对象；这是Adapter模式最重要的功能。使用类的Adapter模式与对象的Adapter模式均可，但感觉使用对象的Adapter模式更简单。
&lt;p&gt;怎么样，够简单吧？难怪我们的CTO如此从容，因为他已经找到了终南捷径！ &lt;/div&gt;&lt;/div&gt;&lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-5487770763778616419&amp;page=RSS%3a+%e4%bb%8e%e5%ae%b9%e8%87%aa%e8%8b%a5%e7%9a%84CTO&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=aspnet.spaces.live.com&amp;amp;GT1=aspnet"&gt;</description><comments>http://aspnet.spaces.live.com/Blog/cns!B3D78590D833939D!327.entry#comment</comments><guid isPermaLink="true">http://aspnet.spaces.live.com/Blog/cns!B3D78590D833939D!327.entry</guid><pubDate>Tue, 07 Nov 2006 09:15:08 GMT</pubDate><slash:comments>2</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://aspnet.spaces.live.com/blog/cns!B3D78590D833939D!327/comments/feed.rss</wfw:commentRss><wfw:comment>http://aspnet.spaces.live.com/Blog/cns!B3D78590D833939D!327.entry#comment</wfw:comment><dcterms:modified>2006-11-07T09:16:14Z</dcterms:modified></item></channel></rss>