阅读内容 

解读设计模式--适配器(Adapter)模式,麻雀飞上枝头真能成凤凰?

[日期:2008-07-02] 来源:  作者: [字体: ]
    
   金庸武侠里出现的有3大神功:北冥神功、化工大法和吸醒大法,这三大神功都是吸取别人内力之功效,却各有异同。 北冥神功 北冥神功的确可以吸人内力化为我用,但并不是所有的北冥神功都可以吸人内力----损人利己。比方说虚竹体内的北冥神功就不可以,这是童姥说的。
  
  化功大法 化功大法是用毒把别人的内力化掉,损失了别人内力却不能为己所用----损人不利己。
  
  吸星大法 吸星大法就是吸掉别人的内力,然后自己用,但不像北冥那样真的和自身内力容为一体,弄不好不属于自己的内力会反扑,伤害到自己----损人又损己。
   在金庸笔下,这三大神功都是难得之宝,多少人为得到他而......,仔细的分析下这三大神功,还是北冥较好,呵呵。我们从软件设计的角度来看,这不知算不算得上是一种复用(功力复用)的思想,只不过有点残忍罢。而在软件设计领域里,"复用"在某些时候也会出现很多问题,比如平台不兼容,开发语言不同或是接口不同等多种原因,弄得不好会不会出现既浪费了别人的现有资源,而自己的系统又无法完成呢?这有点像吸星大法----损人又损己。
  
   企图将设计做好,就能够一劳永逸地坐享其成,这样的愿望就好上面所提到的吸星大法神功一般,这是无效无望的。因为需求的变化最是无常,我们不可能在设计之初将所以的需求变化都考虑彻底,因为我们不是先知,也不是智者,只是略懂设计技术的工匠而已。那我们应该如何应对变化呢?
  
   有没有一套神奇的法决,能在吸取了别人的内力的同时既不损失别人的内力又能将吸取的功力为己所用呢?呵呵,或许正在看本文的你去发明了这套法决后,也会成为与张三丰齐名的一代宗师(看我说到那切了........)。其实在软件设计中也有如同此故事一般的情形出现,比如有一个已设计好的类A(内部有一方法public void Aa(){}),我们需要在现在的系统里去调用他,而系统里却提供的是接口IB(接口下定义有一方法void Bb();),此时,我们要怎么才能让这两个不匹配的接口能够在一起工作呢?
  
   1public class A
   2{
   3 public void Aa()
   4 {
   5 //..逻辑略
   6 }
   7}
   8public interface IB
   9{
  10 void Bb();
  11}
  12
  13//?????????
  14//我们应该怎么才能让A类的Aa方法和IB接口的Bb()方法匹配呢?
   到这里,如果熟悉设计模式的朋友应该已经想到了面向对象里的一个设计模式--适配器(Adapter)模式,他正是用来解决上面所提出的这样的问题。适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作。也有人把这种模式叫做包装(Wrapper)模式。
  
   我们来看看一个很不精确的例子(这是在一位前辈的Blog留言中检到的,具体不记不清楚了),"如虎添翼"是怎么实现的呢?示意性代码如下:
  
  会飞的麻雀
   1namespace DesignPattern.Adapter.FlyingTigers
   2{
   3 /**//// <summary>
   4 /// 麻雀会飞
   5 /// </summary>
   6 public class Sparrow
   7 {
   8 private string name;
   9 public string Name
  10 {
  11 get { return name; }
  12 set { name = value; }
  13 }
  14 public Sparrow() { }
  15 public Sparrow(string name)
  16 {
  17 this.name = name;
  18 }
  19
  20 public void Fly()
  21 {
  22 Console.WriteLine("我是{0},我能飞!", this.Name);
  23 }
  24 }
  25}老虎接口
   1namespace DesignPattern.Adapter.FlyingTigers
   2{
   3 /**//// <summary>
   4 /// 老虎接口
   5 /// </summary>
   6 public interface ITiger
   7 {
   8 void Eat();
   9 }
  10}
   有了一个麻雀的飞行(Fly)接口存在,而现在系统提供的却是一个老虎接口(ITiger-->Eat()),如何才能让这两个不匹配的接口能够在一起工作呢?此时,我们就引入Adapter模式(本示例中采用的是类Adapter模式)。
  
  这是飞天虎吗?
   1namespace DesignPattern.Adapter.FlyingTigers
   2{
   3 //带点多继承的味道--是好?是坏?
   4 public class FlyTiger:Sparrow,ITiger
   5 {
   6 public FlyTiger() { }
   7 public FlyTiger(string name)
   8 {
   9 base.Name = name;
  10 }
  11
  12 public void Eat()
  13 {
  14 base.Fly();
  15 Console.WriteLine("我是{0},我能吃!", base.Name);
  16 }
  17 }
  18}
   上面的"如虎添翼"是这样的吗?
  
  
  其实这就是一个类Adapter模式的UML草图,从图中我们可以清晰的看出,在Adapter模式中,参与角色主要有四种:
  →目标(Target)角色:
   → 这是客户所期待的接口。因为C#不支持多继承,所以在类Adapter模式中Target必须是接口,不可以是类。
  →源(Adaptee)角色:
   → 需要适配的类。
  →适配器(Adapter)角色:
   → 把源接口转换成目标接口。这一角色必须是类。
  →客户端(Client)角色:......
  
  Client Code
   1namespace DesignPattern.Adapter.FlyingTigers
   2{
   3 class Program
   4 {
   5 static void Main(string[] args)
   6 {
   7 //如果是麻雀,麻雀是能飞的.
   8 new Sparrow("麻雀").Fly();
   9
  10 //如果是使用了Adapter的老虎呢?呵呵,能飞能吃,飞天虎吗?
  11 ITiger it = new FlyTiger("老虎");
  12 it.Eat();
  13 }
  14 }
  15}
   或许我们可以这样来总结,类的适配器模式使用起来有点像多重继承机制,但因为多重继承往往带来麻烦,C#中已经不支持了,利用接口的特性,把一些零散类(ITiger、Sparrow)组织到一起,成为一个新的类(FlyTiger)来对实现调用,并且看起来像是对一个类的操作,就是适配器的目的。实际上,适配器模式更多的是强调对代码的组织,而不是功能的实现。
   用通俗而不精确的话来说,为了方便代码的组织与模型的准确表示,这个模式在组织代码的中作用是:可以把一个类中的成员插到另一个类的继承子类中,从而让这个继承的子类看起来像一个新类。
  
   类的Adapter模式通常是通过继承机制来实现,而对象Adapter模式则是使用聚合对象来实现,两者都有优的一面也有劣的一面。详细这里就不多说,给出一句前辈留下的原则大家一起学习“优先使用组合而非继承”。
  
   上面有了三大武林至宝、也有了飞天虎,接下来我们再来看看一个很不精确的例子,如何让麻雀飞上枝头变凤凰。Adapter模式分为类Adapter模式和对象的Adapter模式,至于两者到底该怎么用,什么情况下用什么类型的模式,这还需根据实习的需求而定,如上所说这两种模式的实现机制,在上一个不精确的例子中使用了类的Adapter模式,接下来在让麻雀变凤凰的故事里我们就以对象Adapter模式的方式来实现。代码如下:
  
  凤凰--源(Adaptee)角色:需要适配的类。
   1namespace DesignPattern.Adapter.SparrowPhoenix
   2{
   3 /**//// <summary>
   4 /// 凤凰--源(Adaptee)角色:需要适配的类。
   5 /// </summary>
   6 public class Phoenix
   7 {
   8 public void Display()
   9 {
  10 Console.WriteLine("我是美丽的凤凰,也有人叫我火鸟,是吉祥和谐的象征。");
  11 }
  12 }
  13}目标(Target)角色:这是客户所期待的接口。
   1namespace DesignPattern.Adapter.SparrowPhoenix
   2{
   3 /**//// <summary>
   4 /// 目标(Target)角色:这是客户所期待的接口。
   5 /// 目标可以是具体的或抽象的类,也可以是接口。
   6 /// </summary>
   7 public interface ISparrowPhoenix
   8 {
   9 void Show();
  10 }
  11}麻雀--适配器(Adapter)角色,聚合一个凤凰(Phoenix).
   1namespace DesignPattern.Adapter.SparrowPhoenix
   2{
   3 /**//// <summary>
   4 /// 麻雀--适配器(Adapter)角色,聚合一个凤凰(Phoenix).
   5 /// 通过在内部包装(Wrap)一个Adaptee(源对象)对象,把源接口转换成目标接口.
   6 /// </summary>
   7 public class Sparrow:ISparrowPhoenix
   8 {
   9 private Phoenix phoenix = new Phoenix();
  10 public void Show()
  11 {
  12 phoenix.Display();
  13 }
  14 }
  15} 1namespace DesignPattern.Adapter.SparrowPhoenix
   2{
   3 class Program
   4 {
   5 static void Main(string[] args)
   6 {
   7 ISparrowPhoenix isp = new Sparrow();
   8 isp.Show();//麻雀变凤凰了?????
   9 }
  10 }
  11}
  12
  13
  14运行结果:我是美丽的凤凰,也有人叫我火鸟,是吉祥和谐的象征。
  麻雀变凤凰(对象Adapter模式)的UML草图:
  
  呵呵,原本丑陋的麻雀,通过了Adapter的包装,将其改装为了凤凰的血统,神奇了,他还真变成了"凤凰"勒。现在我们可回头去仔细分析下,给老虎加入了翅膀,他真的变飞虎了吗?给麻雀换上凤凰的血液,麻雀真的变凤凰了吗?
  
  本文示例代码下载:DesignPattern.Adapter.rar
  http://www.cnblogs.com/Files/beniao/DesignPattern/DesignPattern.Adapter.rar  
阅读:
录入:blue1000

推荐 】 【 打印
相关新闻      
本文评论       全部评论
发表评论
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款


点评: 字数
姓名:
Advertisement
内容查询


Advertisement