我们周围的适配器
OO适配器是什么,一定不难理解,因为现实中到处都是。比方说:如果你需要一个两项插头,但墙上只有一个三项插座,这时候你就需要使用一个交流电的适配器……
又或者你需要对通过串口对某设备进行调试,但是你的机器没有串口,只有USB接口,这时候你就需要一个USB转串口的适配器……
这是真实世界的适配器,那面向对象适配器又是什么?其实,OO适配器和真实世界的适配器扮演着同样的角色:将一个接口转换成另一个接口,以符合客户的期望。
面向对象适配器
假设已有一个软件系统,它与旧厂商的某个接口搭配使用。你希望替换掉旧厂商,让它能和一个新厂商类库搭配使用,但是这个新厂商所设计出来的接口,不同于旧厂商的接口。你不想改变现有的代码解决这个问题。所以需要编写一个适配器,这个适配器工作起来就如同一个中间人,它将客户所发出的请求转换成新厂商能理解的请求。
举个例子
以鸭子类为例,鸭子可以飞,并且可以呱呱叫,以下是鸭子类的接口与实现:
public interface Duck { // 呱呱叫 public void quack(); // 飞 public void fly();}public class MallardDuck implements Duck { public void quack() { System.out.println("Quack"); } public void fly() { System.out.println("I'm flying"); }}
接下来再看一个“街头顽禽”火鸡类的接口和实现类:
public interface Turkey { // 火鸡不会呱呱叫,只会咯咯叫 public void gobble(); // 伙计会飞,虽然飞不远 public void fly();}public class WildTurkey implements Turkey { public void gobble() { System.out.println("Gobble gobble"); } public void fly() { System.out.println("I'm flying a short distance"); }}
现在,假设你缺鸭子对象,想用一些火鸡对象冒充。显而易见,因为火鸡的接口不同,所以我们不能公然拿来用。 所以就需要写一个适配器。
也就是说,加入你有一个方法,参数是Duck接口,你想把Turkey接口传递进去。
接下来适配器的代码:
// 首先,适配器需要实现想转换成的接口类型。// 这里想把火鸡转换成鸭子,所以实现了鸭子的接口。public class TurkeyAdapter implements Duck { Turkey turkey; // 接着,需要取得要适配的对象引用。 // 也就是你的客户所期望看到的接口。 // 这里想把火鸡转换成鸭子,所以传入火鸡的接口。 public TurkeyAdapter(Turkey turkey) { this.turkey = turkey; } // 在鸭子叫的方法中调用火鸡叫的方法。 public void quack() { turkey.gobble(); } // 在鸭子飞的方法中调用火鸡飞的方法。 // 因为火鸡飞不远,鸭子飞一次,火鸡需要飞五次。 public void fly() { for (int i = 0; i < 5; i++) { turkey.fly(); } }}
测试适配器:
public class Client { public static void main(String[] args) { // 创建一只鸭子 MallardDuck duck = new MallardDuck(); // 创建一直火鸡 WildTurkey turkey = new WildTurkey(); // 将火鸡包装进一个火鸡适配器,是它看起来像是一只鸭子。 Duck turkeyAdapter = new TurkeyAdapter(turkey); System.out.println("The Turkey says..."); turkey.gobble(); turkey.fly(); System.out.println("\nThe Duck says..."); testDuck(duck); System.out.println("\nThe TurkeyAdapter says..."); testDuck(turkeyAdapter); } static void testDuck(Duck duck) { duck.quack(); duck.fly(); } }
首先让火鸡又叫又飞,接着让鸭子又叫又飞,最后让适配器处理过的火鸡像鸭子一样又叫又飞,quack()被调用时,适配器咯咯叫。testDuck()方法根本不知道,这其实是一只假装成鸭子的火鸡!
适配器模式解析
现在我们已经知道什么是适配器了,让我们退后一步,再次看看各部分之间的关系。‘
客户端调用适配器的过程如下:
- 客户通过目标接口调用适配器的方法对适配器发出请求;
- 适配器使用被适配者接口吧请求转换成被适配者的一个或多个调用接口;
- 客户接收到调用的结果,但并未察觉这一切是适配器在起转换作用;
现在来看看适配器模式的类图:
这个适配器模式充满良好的OO设计原则:使用对象组合,以修改的接口包装被适配者,这种做法还有额外的优点,那就是,被适配者的任何子类,都可以搭配着适配器使用。
在上面的类图中,鸭子类就是Target类,火鸡类就是Adaptee类。
以上就是适配器模式。