《大话设计模式》之--第17章 在NBA我需要翻译----适配器模式

http://blog.csdn.net/monkey_d_meng/article/details/5707827


第17章 在NBA我需要翻译----适配器模式

17.1在NBA我需要翻译!

周日,小菜与大鸟上午在家刚看完NBA季后赛第一场比赛,出去吃饭时。

“大鸟,今天火箭开门红,东得真是爽呀。”小菜感慨万分。

“是呀,希望能把这种势头保持到最后,那就可以有所突破了。”大鸟肯定说。

“你说姚明去了几年,英语也真练出来了哦,我看教练在那里布置战术,他旁边也没有翻译的,不住点头,瞧样子听懂没什么问题了。”

“要知道,在儿年前。有记者问姚明说:‘在CBA和NBA最大的区别是什么?’,姚明的答案是‘在NBA我需要翻译,而在CBA我不需要。’经过四年的NBA锤炼,他的确是在NBA磨练中成长了。不但球技大涨,英语也学得非常棒。用英文答记者问一点问题都没有。不得不佩服呀。”

“钞票也大大地增加了,他可是中国最富有的体育明星。大鸟呀,你比他还大几岁吧,混得不行呀。”

“哪能和他比,两米二七的身高,你给我长一个试试。再说,单有身高也是不行的。在NBA现役中锋中,姚明也算是个天才吧。”

“你说当时他刚去美国时,怎么打球呀,什么都听不懂。”

“姚明爱说的一句名言叫‘皇帝不急急太监’,这种问题不是很容易吗,之前专门为他配备了翻译的,那个翻译一直在姚明身边,特别是比赛场上,教练、队员与他的对话全部都通过翻译来沟通。”

“想想看也是,如果不懂外语,又没有翻译,球技再高,估计也是不可能在国外待很长时间的。”

“哦,你等等,你的这个说法,倒让我想起一个设计模式,非常符合你现在提到的这个场景。”

“是吗,’听大鸟说设计模式已经成为习惯了,听不到都难受。快点说说看。”

17.2适配器模式

“这个模式叫做适配器。”

适配器模式(Adapter),将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

“适配器模式主要解决什么问题?”

“简单地说,就是需要的东西就在面前,但却不能使用,而短时间又无法改造它,于是我们就想办法适配它。”

“前面的听懂了,有东西不能用,又不能改造它。但想办法适配是虾米意思?”

“其实这个词应该是最早出现在电工学里面的,有些国家用110V电压,而我们国家用的是220V,但我们的电器,比如笔记本电脑是不能什么电压都用的,但国家不同,电压可能不相同也是事实,于是就用一个电源适配器,只要是电,不管多少伏,都能把电源变成需要的电压,这就是电源适配器的作用。适配器的意思就是使得一个东西适合另一个东西的东西。”

“这个我明白,但和适配器模式有什么关系?”

“哈,NBA篮球运动员都会打篮球,姚明也会打篮球。”

“废话。”

“你小子,别打岔。但是姚明不会英语,要在美国NBA打球,不会英语如何交流?没有交流如何理解教练和同伴的意图?又如何让他们理解自己的想法?不能沟通就打不好球了。于是就有三个办法,第一,让姚明学会英语,你看如何?”

“这不符合实际啊,姚明刚到NBA打球,之前又没有时间在学校里面认真学好英语,马上学到可以听懂会说的地步是很困难的。”

“说的8错,第二种方法,让教练和球员学会中文?”

“Are you搞笑?”

“是撒,那你说么办?”

“给姚明找个翻译。你的意思是说翻译即是适配器?”

“你丫怎么反应这么迟钝,在我们不能更改球队教练、球员和姚明的前提下,我们能做的就是想办法找个适配器。在软件开发中,也就是系统的数据和行为都是正确的,但接口不符合时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况,比如在需要对早期代码复用一些功能等应用上很有实际价值。”

“在GoF的设计模式中,对适配器模式讲了两种类型,类适配器械和对象适配器模式,由于类适配器模式通过多重继承对一个接口与另一个接口进行匹配,而C#、VB.NET、JAVA等语言都不支持多重继承(C++支持),也就是一个类只有一个父类,所以我们这里主要讲的是对象适配器。”

适配器(Adapter)结构图

[java] view plaincopy
  1. //Target类,客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口  
  2. public class Target  
  3. {  
  4.     public void request()  
  5.     {  
  6.         System.out.println("普通请求!");  
  7.     }  
  8. }  
  9. //Adaptee类,需要适配的类  
  10. public class Adaptee extends Target  
  11. {  
  12.     public void specificRequest()  
  13.     {  
  14.         System.out.println("特殊请求!");  
  15.     }  
  16. }  
  17. //Adapter类,通过在内部包装一个Adaptee对象,把源接口转换成目标接口  
  18. public class Adapter extends Target  
  19. {  
  20.     private Adaptee adaptee = new Adaptee();  
  21.   
  22.     public void request()  
  23.     {  
  24.         adaptee.specificRequest();  
  25.     }  
  26. }  
  27. //客户端代码  
  28. public class Main  
  29. {  
  30.     public static void main(String[] args)  
  31.     {  
  32.         Target target = new Adapter();  
  33.         target.request();  
  34.     }  
  35. }  

17.3何时使用适配器模式

“你的意思是不是说,在想使用一个已经存在的类,但如果它的接口,也就是它的方法和你的要求不相同时,就应该考虑使用适配器模式?”

“对的,两个类所做的事情相同或相似,但具有不同的接口时就要使用它。而且由于类都共享同一个接口,使得客户代码如何?”

“客户代码可以统一调用同一接口就可以了,这样应该可以更简单、更直接、更紧凑。”

“很好,其实用适配器模式也是无奈之举,很有点亡羊补牢的感觉,没办法啊,是软件就有维护的一天,维护就有可能会因不同的开发人员、不同的产品、不同的厂家而造成功能类似而接口不同的情况,此时就是适配器模式大展拳脚的时候了。”

“你的意思是说,我们通常是在软件开发后期或维护期才考虑使用它吗?”

“如果是在设计阶段,你有必要把类似的功能类的接口设计的不同吗?”

“话虽然这样说,但是不同的程序员定义的方法的名称可能不同啊。”

“首先,公司内部,类和方法的命名应该有规范,最好前期就设计好,然后,如果直接的如你所说的,接口不相同时,首先不应该考虑用适配器,而是应该考虑通过重构统一接口。”

“明白了,就是要在双方都不太容易修改的时候再使用适配器模式适配,而不是一有不同时就使用它。那有没有设计之初就需要考虑用适配器模式的?”

“当然有,比如公司设计一系统时考虑使用第三方开发组件,而这个组件的接口与我们自己的系统接口是不相同的,而我们也完全没有必要为了迎合它而改动自己的接口,此时尽管是在开发的设计阶段,也是可以考虑用适配器模式来解决接口不同的问题。好了,说了这么多,你都没有练习一下,来来来,试着把刚才火箭队的比赛,教练叫暂停时给后卫、中锋、前锋分配进攻和防守任务的代码模拟出来。”

17.4篮球翻译适配器

“哈,这有何难。后卫、中锋、前锋都是球员,所以应该有一个球员抽象类,有进攻和防守的方法。”

 

[java]  view plain copy
  1. //球员类  
  2. public abstract class Player  
  3. {  
  4.     protected String    name;  
  5.   
  6.     public Player(String name)  
  7.     {  
  8.         this.name = name;  
  9.     }  
  10.   
  11.     public abstract void attack();  
  12.   
  13.     public abstract void defense();  
  14. }  
  15. //后卫、中锋、前锋类  
  16. public class Forwards extends Player  
  17. {  
  18.     public Forwards(String name)  
  19.     {  
  20.         super(name);  
  21.     }  
  22.   
  23.     public void attack()  
  24.     {  
  25.         System.out.println("前锋" + name + "进攻");  
  26.     }  
  27.   
  28.     public void defense()  
  29.     {  
  30.         System.out.println("前锋" + name + "防守");  
  31.     }  
  32. }  
  33. public class Center extends Player  
  34. {  
  35.     public Center(String name)  
  36.     {  
  37.         super(name);  
  38.     }  
  39.   
  40.     public void attack()  
  41.     {  
  42.         System.out.println("中锋" + name + "进攻");  
  43.     }  
  44.   
  45.     public void defense()  
  46.     {  
  47.         System.out.println("中锋" + name + "防守");  
  48.     }  
  49. }  
  50. public class Guards extends Player  
  51. {  
  52.     public Guards(String name)  
  53.     {  
  54.         super(name);  
  55.     }  
  56.   
  57.     public void attack()  
  58.     {  
  59.         System.out.println("后卫" + name + "进攻");  
  60.     }  
  61.   
  62.     public void defense()  
  63.     {  
  64.         System.out.println("后卫" + name + "防守");  
  65.     }  
  66. }  
  67. //客户端代码  
  68. public class Main  
  69. {  
  70.     public static void main(String[] args)  
  71.     {  
  72.         Player b = new Forwards("巴蒂尔");  
  73.         b.attack();  
  74.   
  75.         Player m = new Guards("麦克格雷迪");  
  76.         m.attack();  
  77.   
  78.         Player ym = new Center("姚明");  
  79.         ym.attack();  
  80.         ym.defense();  
  81.     }  
  82. }  
  83. 结果显示:  
  84. 前锋巴蒂尔进攻  
  85. 后卫麦克格雷迪进攻  
  86. 中锋姚明进攻  
  87. 中锋姚明防守  

“注意,姚明刚来到NBA,他身高够高,球技够好,但是,他那个时候听不懂英语,也就是说,他听不懂教练的战术安排,attack和defense是什么意思都不知道。你这样的写法就是有问题的。事实上,当时是如何解决这个矛盾的?”

“姚明说我需要翻译。我知道你的意思了,姚明是外籍中锋,需要有翻译者类也适配。”

[java]  view plain copy
  1. //外籍中锋  
  2. public class ForeignCenter  
  3. {  
  4.     private String  name;  
  5.   
  6.     public void 进攻()  
  7.     {  
  8.         System.out.println("外籍中锋" + name + "进攻");  
  9.     }  
  10.   
  11.     public void 防守()  
  12.     {  
  13.         System.out.println("外籍中锋" + name + "防守");  
  14.     }  
  15.   
  16.     public String getName()  
  17.     {  
  18.         return name;  
  19.     }  
  20.   
  21.     public void setName(String name)  
  22.     {  
  23.         this.name = name;  
  24.     }  
  25. }  
  26. //翻译者类  
  27. public class Translator extends Player  
  28. {  
  29.     private ForeignCenter   wjzf    = new ForeignCenter();  
  30.   
  31.     public Translator(String name)  
  32.     {  
  33.         super(name);  
  34.         wjzf.setName(name);  
  35.     }  
  36.   
  37.     public void attack()  
  38.     {  
  39.         wjzf.进攻();  
  40.     }  
  41.   
  42.     public void defense()  
  43.     {  
  44.         wjzf.防守();  
  45.     }  
  46. }  
  47. //客户端代码不变  
  48. public class Main  
  49. {  
  50.     public static void main(String[] args)  
  51.     {  
  52.         Player b = new Forwards("巴蒂尔");  
  53.         b.attack();  
  54.   
  55.         Player m = new Guards("麦克格雷迪");  
  56.         m.attack();  
  57.   
  58.         Player ym = new Translator("姚明");  
  59.         ym.attack();  
  60.         ym.defense();  
  61.     }  
  62. }  
  63. 结果显示:  
  64. 前锋巴蒂尔进攻  
  65. 后卫麦克格雷迪进攻  
  66. 外籍中锋姚明进攻  
  67. 外籍中锋姚明防守  

 

代码结构图

 

大鸟:“这下好了,尽管姚明曾经是不太懂英文,尽管火箭教练和球员也不会学中文,但因为有了翻译者,团队沟通合作成为了可能,非常好。”

17.5适配器模式的.NET应用

“这模式很好用.我想在现实中也很常用的吧。”

“当然,比如在.NET中有一个类库已经实现的、非常重要的适配器,那就是DataAdaptero DataAdapter用作DataSet和数据源之间的适配器以便检索和保存数据。DataAdapter通过映射Fib(这更改了DataSet中的数据以便与数据源中的数据相匹配)和Update(这更改了数据源中的数据以便与DataSet中的数据相匹配)来提供这一适配器[MSDN]。由于数据源可能是来自SQL Server,可能来自Oracle,也可能来自Access、DB2,这些数据在组织上可能有不同之处,但我们希望得到统一的DataSet(实质是XML数据),此时用DataAdagter就是非常好的手段,我们不必关注不同数据库的数据细节,就可以灵活的使用数据。”

“啊,DataAdagter我都用了无数次了.原来它就是适配器模式的应用呀,太棒了。我喜欢这个模式,我要经常性地使用它。”

“NOP啦!模式乱用不如不用。我给你讲个小故事吧,希望你能理解其深意。”

17.6扁鹊的医术

 “当年,魏文王问名医扁鹊说:‘你们家兄弟三人,都精于医术,到底哪一位最好呢?’扁鹊答:‘长兄最好。中兄次之,我最差。’文王再问:‘那么为什么你最出名呢?’扁鹊答:‘长兄治病,是治病于病情发作之前。由于一般人不知道他事先能铲除病因,所以他的名气无法传出去;中兄治病,是治病于病情初起时。一般人以为他只能治轻微的小病,所以他的名气只及本乡里。而我是治病于病情严重之时。一般人都看到我在经脉上穿针管放血、在皮肤上敷药等大手术,所以大家都以为我的医术高明,名气因此响遍全国。’这个故事说明什么?”

“啊,你的意思是如果能事先预防接口不同的问题,不匹配问题就不会发生在有小的接口不统一问题发生时,及时重构,问题不至于扩大;只有碰到无法改变原有设计和代码的情况时,才考虑适配。事后控制不如事中控制,事中控制不如事前控制。”小菜总结说。

“对呀,如果能事前控制,又何必要事后再去弥补呢?”大鸟肯定说,“适配器模式当然是好模式,但如果无视它的应用场合而盲目便用,其实是本末倒置了。”

“嗯,我相信,小菜我能把适配器模式应用到扁鹊的境界的。哦,不对,是他们三兄弟共同的境界。我一定能!”

“相信你一定能!”


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值