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

    金庸武侠里出现的有3大神功:北冥神功、化工大法和吸醒大法,这三大神功都是吸取别人内力之功效,却各有异同。
北冥神功       北冥神功的确可以吸人内力化为我用,但并不是所有的北冥神功都可以吸人内力----损人利己。比方说虚竹体内的北冥神功就不可以,这是童姥说的。

     
化功大法       化功大法是用毒把别人的内力化掉,损失了别人内力却不能为己所用----损人不利己。


吸星大法       吸星大法就是吸掉别人的内力,然后自己用,但不像北冥那样真的和自身内力容为一体,弄不好不属于自己的内力会反扑,伤害到自己----损人又损己。

      在金庸笔下,这三大神功都是难得之宝,多少人为得到他而......,仔细的分析下这三大神功,还是北冥较好,呵呵。我们从软件设计的角度来看,这不知算不算得上是一种复用(功力复用)的思想,只不过有点残忍罢。而在软件设计领域里,"复用"在某些时候也会出现很多问题,比如平台不兼容,开发语言不同或是接口不同等多种原因,弄得不好会不会出现既浪费了别人的现有资源,而自己的系统又无法完成呢?这有点像吸星大法----损人又损己。

      企图将设计做好,就能够一劳永逸地坐享其成,这样的愿望就好上面所提到的吸星大法神功一般,这是无效无望的。因为需求的变化最是无常,我们不可能在设计之初将所以的需求变化都考虑彻底,因为我们不是先知,也不是智者,只是略懂设计技术的工匠而已。那我们应该如何应对变化呢?

      有没有一套神奇的法决,能在吸取了别人的内力的同时既不损失别人的内力又能将吸取的功力为己所用呢?呵呵,或许正在看本文的你去发明了这套法决后,也会成为与张三丰齐名的一代宗师(看我说到那切了........)。其实在软件设计中也有如同此故事一般的情形出现,比如有一个已设计好的类A(内部有一方法public void Aa(){}),我们需要在现在的系统里去调用他,而系统里却提供的是接口IB(接口下定义有一方法void Bb();),此时,我们要怎么才能让这两个不匹配的接口能够在一起工作呢?

 1 public   class  A
 2 {
 3    public void Aa()
 4    {
 5        //..逻辑略
 6    }

 7}

 8 public   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)角色:......


 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)角色:需要适配的类。
目标(Target)角色:这是客户所期待的接口。
麻雀--适配器(Adapter)角色,聚合一个凤凰(Phoenix).
 1 namespace  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://beniao.cnblogs.com/  或  http://www.cnblogs.com/




适配器模式定义:
将两个不兼容的类纠合在一起使用,属于结构型模式,需要有Adaptee(被适配者)和Adaptor(适配器)两个身份.

为何使用?
我们经常碰到要将两个没有关系的类组合在一起使用,第一解决方案是:修改各自类的接口,但是如果我们没有源代码,或者,我们不愿意为了一个应用而修改各自的接口。 怎么办?

使用Adapter,在这两种接口之间创建一个混合接口(混血儿).

如何使用?
实现Adapter方式,其实"think in Java"的"类再生"一节中已经提到,有两种方式:组合(composition)和继承(inheritance).


假设我们要打桩,有两种类:方形桩 圆形桩.
public class SquarePeg{
  public void insert(String str){
    System.out.println("SquarePeg insert():"+str);
  }

}

public class RoundPeg{
  public void insertIntohole(String msg){
    System.out.println("RoundPeg insertIntoHole():"+msg);
}
}

现在有一个应用,需要既打方形桩,又打圆形桩.那么我们需要将这两个没有关系的类综合应用.假设RoundPeg我们没有源代码,或源代码我们不想修改,那么我们使用Adapter来实现这个应用:

public class PegAdapter extends SquarePeg{

  private RoundPeg roundPeg;

  public PegAdapter(RoundPeg peg)(this.roundPeg=peg;)

  public void insert(String str){super.insert(str); roundPeg.insertIntoHole(str);}

}

在上面代码中,RoundPeg属于Adaptee,是被适配者.PegAdapter是Adapter,将Adaptee(被适配者RoundPeg)和Target(目标SquarePeg)进行适配.实际上这是将组合方法(composition)和继承(inheritance)方法综合运用.

PegAdapter首先继承SquarePeg,然后使用new的组合生成对象方式,生成RoundPeg的对象roundPeg,再重载父类insert()方法。从这里,你也了解使用new生成对象和使用extends继承生成对象的不同,前者无需对原来的类修改,甚至无需要知道其内部结构和源代码.

如果你有些Java使用的经验,已经发现,这种模式经常使用。

进一步使用
上面的PegAdapter是继承了SquarePeg,如果我们需要两边继承,即继承SquarePeg 又继承RoundPeg,因为Java中不允许多继承,但是我们可以实现(implements)两个接口(interface)

public interface IRoundPeg{
  public void insertIntoHole(String msg);

}

public interface ISquarePeg{
  public void insert(String str);

}

下面是新的RoundPeg 和SquarePeg, 除了实现接口这一区别,和上面的没什么区别。
public class SquarePeg implements ISquarePeg{
  public void insert(String str){
    System.out.println("SquarePeg insert():"+str);
  }

}

public class RoundPeg implements IRoundPeg{
  public void insertIntohole(String msg){
    System.out.println("RoundPeg insertIntoHole():"+msg);
  }
}

下面是新的PegAdapter,叫做two-way adapter:

public class PegAdapter implements IRoundPeg,ISquarePeg{

  private RoundPeg roundPeg;
  private SquarePeg squarePeg;

  // 构造方法
  public PegAdapter(RoundPeg peg){this.roundPeg=peg;}
  // 构造方法
  public PegAdapter(SquarePeg peg)(this.squarePeg=peg;)

  public void insert(String str){ roundPeg.insertIntoHole(str);}

}

还有一种叫Pluggable Adapters,可以动态的获取几个adapters中一个。使用Reflection技术,可以动态的发现类中的Public方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值