软件开发中经常遇到的问题:
假设已有一个软件系统,你希望它能和一个新的厂商类库搭配使用,但是这个新厂商所设计出来的接口,不同于旧厂商的接口。如果你不想改变现有的代码,也不能改变厂商的代码,那么你该如何解决这个问题。方法其实很简单,就是写一个类,将新厂商的的接口转成你所期望的接口。如图
这就是适配器模式。
意图:将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
下面我们来看一个具体的例子来阐释对象适配器:
先准备一只鸭子
2 {
3 void Quack(); // 鸭子会呱呱叫
4 void Fly(); // 鸭子会飞
5 }
再准备一只绿头鸭
2 {
3 public void Quack()
4 {
5 Console.WriteLine("Quack");
6 }
7
8 public void Fly()
9 {
10 Console.WriteLine("I'm flying");
11 }
12 }
准备一只火鸡
2 {
3 void Gobble(); // 火鸡不会呱呱叫,只会咯咯叫
4 void Fly(); // 火鸡会飞
5 }
准备一只野性十足的火鸡
2 {
3 public void Gobble()
4 {
5 Console.WriteLine("Gobble,gobble");
6 }
7 public void Fly()
8 {
9 Console.WriteLine("I'm flying a short distance");
10 }
11 }
现在,假设你缺鸭子对象,想用一些火鸡对象冒充鸭子,那么你该如何做呢?显然我们的问题是火鸡的接口和鸭子的接口不一样,那么我们可以通过写一个适配器的方法来解决。具体代码如下:
2 {
3 ITurkey turkey;
4
5 public TurkeyAdapter(ITurkey turkey)
6 {
7 this.turkey = turkey; // 火鸡实例化
8 }
9
10 public void Quack()
11 {
12 turkey.Gobble(); // 火鸡冒充鸭子的叫声
13 }
14
15 public void Fly()
16 {
17 turkey.Fly(); // 火鸡冒充鸭子飞行
18 }
19 }
现在我们来写一段测试程序:
2 {
3 MallardDuck duck = new MallardDuck();
4 WildTurkey turkey = new WildTurkey();
5 IDuck turkeyAdapter = new TurkeyAdapter(turkey);
6
7 Console.WriteLine("The Turkey says ");
8 turkey.Gobble();
9 turkey.Fly();
10 Console.WriteLine("The Duck says ");
11 testDuck(duck);
12 Console.WriteLine("The TurkeyAdapter says ");
13 testDuck(turkeyAdapter);
14
15 Console.ReadLine();
16 }
17
18 static void testDuck(IDuck duck)
19 {
20 duck.Quack();
21 duck.Fly();
22 }
效果
适配器模式解析:
从上面的图中可以看出客户使用适配器的过程:
1)、客户通过目标接口调用适配器的方法对适配器发出请求。
2)、适配器使用被适配者接口把请求转化成被适配者的一个或多个调用接口。
3)、客户接收到调用的结果,但并未察觉这是适配器在起转换作用。
应该注意的地方:
1)、适配器需要做的工作与目标接口的大小成正比,接口越大,需要做的工作越多。
2)、根据实际需要我们可以让适配器实现好几个接口。一句话,要灵活使用模式。
类适配器的代码与对象适配器的代码类似,可以参考结构图仿照着写出来,这里不赘述。
下面说说对象适配器与类似配器的区别:
1)、对象适配器使用组合,更富有弹性;类适配器使用继承。
2)、对象适配器可以适配被适配者的子类;类适配器只能覆盖被适配者的行为。
适配器模式的适用性:
1)、你想使用一个已经存在的类,而它的接口不符合你的需求。
2)、你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
3)、你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。