设计模式-结构型之适配器模式
2015-08-27 22:13
120 查看
模式动机
适配是指“源”到“目标”能够匹配、兼容,两者之间的中间件就叫适配器。适配器主要是起到“源”到“目标”的一个过渡作用,负责把“源”转换成我们想要的“目标”。比如,假设我们系统中现在存在了一个Bird(鸟)类,Bird里面有自己的一个fly()方法,还存在一个Tiger(老虎)类,Tiger里面有自己的一个run()方法。同时,我们定义了一个Moveable接口(把“可移动”这种行为抽象出来的一个标准接口),里面有一个抽象方法move(),客户端程序是调用的标准接口Moveable,现在客户端希望通过这个标准接口让鸟儿和老虎移动起来,很显然,鸟儿移动是靠飞翔,老虎移动是靠奔跑,此时我们我们应该怎么办,如何能将两者适配起来?当然,可能都会首先想到修改Bird跟Tiger的代码,让其实现Moveable接口不就行了么。但假如Bir跟Tiger是引入的外部的程序,我们没有源码呢?同时,即使可以修改源码,这么做也有悖于高内聚低耦合的原则。此时,我们便需要使用适配器模式来解决!
模式定义
将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)类适配器
适配器模式有两种实现,一种是类适配器,另一种是对象适配器。首先介绍类适配器,类适配器的结构如下:Target为我们的目标接口,Adaptee是我们的”源“,也就是适配者,Adapter则为两者之间的适配器。直接看代码:
//这个是"源",相当于类图中的Adaptee,我们希望能够转换成我们的目标接口Moveable public class Bird { public void fly(){ System.out.println("我是鸟,我飞翔"); } }
//这个是目标,相当于类图中的Target,是用户所期望的标准接口 public interface Moveable { public abstract void move(); }
//这个便是适配器,相当于类图中的Adapter public class BirdAdapter extends Bird implements Moveable{ @Override public void move() { this.fly(); } }
//客户端程序,控制台打印出:我是鸟,我飞翔 public class Client { public static void main(String[] args) { Moveable m1 = new BirdAdapter(); m1.move(); } }
可以看到,我们的适配器BirdAdapter继承了Bird类,然后实现了Moveable接口,然后在重写move方法时调用”源“的具体方法,达到了适配的效果。但问题来了,我们还需要适配Tiger类,甚至还有更多。显然,由于java是单继承,而我们的代码中BirdAdapter已经继承了Bird类,所以我们只能再另外写一个适配器TigerAdapter。也就是说我们的这个适配器只能为Bird一个类服务,所以这种适配器就叫类适配器。
对象适配器
接下来看看什么是对象适配器,直接看代码:Bird类跟Moveable接口代码都不变,只是适配器类的代码需要改变,如下:
public class Adapter implements Moveable{ private Bird bird; public Adapter(Bird bird){ this.bird = bird; } @Override public void move() { bird.fly(); } }
可以看到,此时的适配器类不再继承Bird类,而是把bird作为一个成员变量,然后调用bird.fly(),这样就不再受java单继承的限制,可以对多个”源“进行适配。举个例子,假设现在还有一个Tiger类,老虎类代码如下:
public class Tiger { public void run(){ System.out.println("我是老虎,我奔跑"); } }
现在还需要对Tiger进行适配,那么我们的适配器Adapter就可以这样写:
public class Adapter implements Moveable{ private Object obj; public Adapter(Object obj){ this.obj = obj; } @Override public void move() { if(obj instanceof Bird){ ((Bird) obj).fly(); }else if(obj instanceof Tiger){ ((Tiger) obj).run(); } } }
客户端根据需要传入相应的”源“对象即可。
public class Client { public static void main(String[] args) { Moveable m = new Adapter(new Tiger()); m.move(); } }
这便是对象适配器,它把“源”作为一个构造参数传入适配器,然后执行接口所要求的方法。这种适配模式可以为多个源进行适配。弥补了类适配模式的不足。
模式延伸-默认适配器
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。因此也称为单接口适配器模式。简单举例如下:public interface Target { public abstract void m1(); public abstract void m2(); public abstract void m3(); }
//默认适配器 public class DefaultAdapter implements Target{ @Override public void m1() { } @Override public void m2() { } @Override public void m3() { } }
public class Concrete extends DefaultAdapter{ @Override public void m3(){ System.out.println("我只想重写m3方法,其他的我不想管"); } }
适用场景
系统需要使用现有的类,而这些类的接口不符合系统的需要想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
相关文章推荐
- iOS:转载:UIControl的使用
- Web Api 简介
- 按行统计符合条件的列数
- LeetCode:Palindrome Linked List
- 【一些事晚报】B站价值17亿??
- 测试的核心技术
- keytool相关命令
- HDU 1087 (LIS)
- 有关cs与bs的结构测试
- hdu 4272 LianLianKan(模拟dfs+map)
- ZOJ 1115解题报告
- iOS中的四中触摸事件的详解 - 平移- 捏合 - 滑动(TouchesBegan,touchesMoved,touchesEnded,touchesCancelled)
- POJ3254--Corn Fields
- zTree初体验(四)——节点增删改
- 求集合元素的所有非空子集
- 备忘录模式
- dlopen Linux 动态库失败原因与解决办法总结
- 敏捷软件开发:原则、模式与实践——第10章 LSP:Liskov替换原则
- 阿里 Java面试 知识点
- Android学习0827<九>(Toast、AlertDialog )