设计模式学习---第一节:适配器模式

转自:http://haolloyin.blog.51cto.com/1177454/338671


有人认为讲解设计模式的例子都太简单,看着感觉是那么回事,但是要是真想在项目开发中使用,还真是应用不到。其实我们不必在项目中刻意使用设计模式,而是应该从实际的设计问题出发,看哪个模式能解决我们的问题,就使用哪个模式。不要为了使用模式而使用模式,那样就舍本逐末了,一般情况下,只要遵循一定的设计原则就可以了,设计模式也是根据这些原则被总结出来的,熟悉了这些原则,模式自然而然就有了。

一般情况:适配器模式不适合在详细设计阶段使用它,它是一种补偿模式,专用来在系统后期扩展、修改时所用。


适配器模式(Adapter):适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配无法在一起工作的两个类可以在一起工作。


适配器模式是结构型设计模式之一,其在不修改原来两个模块代码的情况下,将两个不兼容的类融合在一起,有点像粘合剂,通过转换使得他们能够协作起来, 总结起来就是对扩展开放和对修改关闭的开闭原则,符合了开闭原则.

适配器模式的三个特点:
1 适配器对象实现原有接口
2 适配器对象组合一个实现新接口的对象(这个对象也可以不实现一个接口,只是一个单纯的对象)

3 对适配器原有接口方法的调用被委托给新接口的实例的特定方法

适用场景:

1、已经存在的类的接口不符合我们的需求;

2、创建一个可以复用的类,使得该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作;

3、在不对每一个都进行子类化以匹配它们的接口的情况下,使用一些已经存在的子类。


对比
类适配器模式使用的是继承的方式,而对象适配器模式则使用的是组合的方法。从设计模式的角度来说,对象适配器模式遵循 OO 设计原则的“多用组合,少用继承”,这是一个优点,但是类适配器模式有一个好处是它不需要重新实现整个被适配者的行为,毕竟类适配器模式使用的是继承的方式,当然这么做的坏处就是失去了使用组合的弹性。
所以在实际过程中需要根据使用情况而定,如果 Adaptee 类的行为很复杂,但是 Adapter 适配器类并不需要这些大部分的无关行为,那么使用对象适配器模式是合适的,但是如果需要重新实现大部分 Adaptee 的行为,那么就要考虑是否使用类适配器模式了。


类适配器和对象适配器的选择:
    从实现上:类适配器使用对象继承的方式,属于静态的定义方式。对象适配器使用对象组合的方式,属于动态组合的方式;
    从工作模式上:类适配器直接继承了 Adaptee,使得适配器不能和 Adaptee 的子类一起工作。对象适配器允许一个 Adapter 和多个 Adaptee,
    包括 Adaptee 和它所有的子类一起工作;
    从定义角度:类适配器可以重定义 Adaptee 的部分行为,相当于子类覆盖父类的部分实现方法。对象适配器要重定义 Adaptee 很困难;
    从开发角度:类适配器仅仅引入了一个对象,并不需要额外的引用来间接得到 Adaptee。对象适配器需要额外的引用来间接得到 Adaptee。
总的来说,建议使用对象适配器方式。

适配器模式分为类适配器模式和对象适配器模式,类适配器通过继承,是静态的定义方式. 
而对象适配器通过代理,使用的是对象组合,是动态组合的方式。

适配器模式的具体应用:

JDK 中有大量使用适配器模式的案例,清单 11 大致列举了一些类。

清单 11. 使用适配器模式的类
java.util.Arrays#asList()
javax.swing.JTable(TableModel)
java.io.InputStreamReader(InputStream)
java.io.OutputStreamWriter(OutputStream)
javax.xml.bind.annotation.adapters.XmlAdapter#marshal()
javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()
Java I/O 库大量使用了适配器模式,例如 ByteArrayInputStream 是一个适配器类,它继承了 InputStream 的接口,并且封装了一个 byte 数组。换言之,它将一个 byte 数组的接口适配成 InputStream 流处理器的接口。

Spring 中使用适配器模式的典型应用
在 Spring 的 AOP 里通过使用的 Advice(通知)来增强被代理类的功能。Spring 实现这一 AOP 功能的原理就使用代理模式(1、JDK 动态代理。2、CGLib 字节码生成技术代理。)对类进行方法级别的切面增强,即,生成被代理类的代理类,并在代理类的方法前,设置拦截器,通过执行拦截器中的内容增强了代理方法的功能,实现的面向切面编程。

Advice(通知)的类型有:BeforeAdvice、AfterReturningAdvice、ThrowSadvice 等。每个类型 Advice(通知)都有对应的拦截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。Spring 需要将每个 Advice(通知)都封装成对应的拦截器类型,返回给容器,所以需要使用适配器模式对 Advice 进行转换。具体代码如清单 16-19 所示。

适配器模式使用注意事项:
充当适配器角色的类就是:实现已有接口的抽象类;
为什么要用抽象类?此类是不要被实例化的。而只充当适配器的角色,也就为其子类提供了一个共同的接口,但其子类又可以将精力只集中在其感兴趣的地方。
适配器模式中被适配的接口 Adaptee 和适配成为的接口 Target 是没有关联的,Adaptee 和 Target 中的方法既可以是相同的,也可以是不同的。
适配器在适配的时候,可以适配多个 Apaptee,也就是说实现某个新的 Target 的功能的时候,需要调用多个模块的功能,适配多个模块的功能才能满足新接口的要求。
适配器有一个潜在的问题,就是被适配的对象不再兼容 Adaptee 的接口,因为适配器只是实现了 Target 的接口。这导致并不是所有 Adaptee 对象可以被使用的地方都能是使用适配器,双向适配器解决了这个问题。

优点
适配器模式也是一种包装模式,它与装饰模式同样具有包装的功能,此外,对象适配器模式还具有委托的意思。总的来说,适配器模式属于补偿模式,专用来在系统后期扩展、修改时使用。

缺点

过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。


一、类适配器



通用类图:

 

我们生活中常常听到的是

电源适配器,它是用于电流变换(整流)的设备。适配器的存在,就是为了将已存在的东西(接口)转换成适合我们的需要、能被我们所利用。在现实生活中,适配器更多的是作为一个中间层来实现这种转换作用。

在上面的通用类图中,Cient 类最终面对的是 Target 接口(或抽象类),它只能够使用符合这一目标标准的子类;而 Adaptee 类则是被适配的对象(也称 源角色),因为它包含specific (特殊的)操作、功能等,所以我们想要在自己的系统中使用它,将其转换成符合我们标准的类,使得 Client 类可以在透明的情况下任意选择使用 ConcreteTarget 类或是具有特殊功能的 Adatee 类。

代码实现如下:

// 已存在的、具有特殊功能、但不符合我们既有的标准接口的类
class Adaptee {
	public void specificRequest() {
		System.out.println("被适配类具有 特殊功能...");
	}
}


// 目标接口,或称为标准接口
interface Target {
	public void request();
}

// 具体目标类,只提供普通功能
class ConcreteTarget implements Target {
	public void request() {
		System.out.println("普通类 具有 普通功能...");
	}
}

 

// 适配器类,继承了被适配类,同时实现标准接口
class Adapter extends Adaptee implements Target{
	public void request() {
		super.specificRequest();
	}
}

 

// 测试类
public class Client {
	public static void main(String[] args) {
		// 使用普通功能类
		Target concreteTarget = new ConcreteTarget();
		concreteTarget.request();
		
		// 使用特殊功能类,即适配类
		Target adapter = new Adapter();
		adapter.request();
	}
}

 测试结果:

普通类 具有 普通功能...
被适配类具有 特殊功能...

 上面这种实现的适配器称为类适配器,因为 Adapter 类既继承了 Adaptee (被适配类),也实现了 Target 接口(因为 Java 不支持多继承,所以这样来实现),在 Client 类中我们可以根据需要选择并创建任一种符合需求的子类,来实现具体功能。


二、对象适配器。



另外一种适配器模式是对象适配器,它不是使用多继承或继承再实现的方式,而是使用直接关联,或者称为委托的方式,类图如下:

 

代码实现如下:

// 适配器类,直接关联被适配类,同时实现标准接口
class Adapter implements Target{
	// 直接关联被适配类
	private Adaptee adaptee;
	
	// 可以通过构造函数传入具体需要适配的被适配类对象
	public Adapter (Adaptee adaptee) {
		this.adaptee = adaptee;
	}
	
	public void request() {
		// 这里是使用委托的方式完成特殊功能
		this.adaptee.specificRequest();
	}
}


// 测试类
public class Client {
	public static void main(String[] args) {
		// 使用普通功能类
		Target concreteTarget = new ConcreteTarget();
		concreteTarget.request();
		
		// 使用特殊功能类,即适配类,
		// 需要先创建一个被适配类的对象作为参数
		Target adapter = new Adapter(new Adaptee());
		adapter.request();
	}
}

 测试结果与上面的一致。从类图中我们也知道需要修改的只不过就是 Adapter 类的内部结构,即 Adapter 自身必须先拥有一个被适配类的对象,再把具体的特殊功能委托给这个对象来实现。使用对象适配器模式,可以使得 Adapter 类(适配类)根据传入的 Adaptee 对象达到适配多个不同被适配类的功能,当然,此时我们可以为多个被适配类提取出一个接口或抽象类。这样看起来的话,似乎对象适配器模式更加灵活一点。

小结:

1、适配器模式也是一种包装模式,与Decorator 装饰模式同样具有包装的功能;此外,对象适配器模式还具有显式委托的意思在里面(其实类适配器也有这种意思,只不过比较隐含而已),那么我在认为它与 Proxy 代理模式也有点类似;

2、从上面一点对比来看, Decorator 、 Proxy、 Adapter 在实现了自身的最主要目的(这个得看各个模式的最初动机、描述)之外,都可以在包装的前后进行额外的、特殊的功能上的增减,因为我认为它们都有委托的实现意思在里面;

3、我所看的书中说适配器模式不适合在详细设计阶段使用它,它是一种补偿模式,专用来在系统后期扩展、修改时所用。


三、更为详细的解释示例:

一个示例让你明白适配器模式:

http://blog.csdn.net/zhangjg_blog/article/details/18735243


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值