适配器模式(Adapter)

1.简述

适配器模式就是将一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。下面这个例子很好的说明了适配器的作用

适配器模式(Adapter)


2.角色

  •  目标角色(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
  • 源角色(Adaptee)也就是被适配的类:需要适配的类或适配者类。
  • 适配器角色(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。

3.两种实现方式和UML类图

(1).类的适配器模式(采用继承实现)

 适配器模式(Adapter)

(2).对象适配器(采用对象组合方式实现)

适配器模式(Adapter)

4.通用代码

(1).类适配器通用代码:

/*
 *	目标角色
 */
interface Target {
	// 目标角色有自己的方法
	public void request();
}

/*
 * 目标角色的实现类
 */
class ConcreteTarget implements Target {

	public void request() {
		System.out.println("我是目标角色的业务逻辑");
	}
}

/*
 * 源角色
 */
class Adaptee {
	// 原有的业务逻辑
	public void doSomething() {
		System.out.println("我是被适配角色的业务逻辑");
	}
}

/*
 * 适配器角色
 */
class Adapter extends Adaptee implements Target {
	public void request() {
		super.doSomething();
	}
}

public class Client {
	public static void main(String[] args) {
		// 如果要使用原有的业务逻辑
		Target target = new ConcreteTarget();
		target.request();
		// 现在增加了适配器角色后的业务逻辑
		Target target1 = new Adapter();
		target1.request();
	}
}

上面这种实现的适配器称为类适配器,因为 Adapter 类既继承了 Adaptee (被适配类),也实现了 Target 接口(因为 Java 不支持多继承,所以这样来实现),在 Client 类中我们可以根据需要选择并创建任一种符合需求的子类,来实现具体功能。另外一种适配器模式是对象适配器,它不是使用多继承或继承再实现的方式,而是使用直接关联,或者称为委托的方式。我们来看下对象适配器的通用代码。

(2).对象适配器通用代码:

/*
 *	目标角色
 */
interface Target {
	// 目标角色有自己的方法
	public void request();
}

/*
 * 目标角色的实现类
 */
class ConcreteTarget implements Target {

	public void request() {
		System.out.println("我是目标角色的业务逻辑");
	}
}

/*
 * 源角色
 */
class Adaptee {
	// 原有的业务逻辑
	public void doSomething() {
		System.out.println("我是被适配角色的业务逻辑");
	}
}

//适配器类,直接关联被适配类,同时实现标准接口
class Adapter implements Target {
	// 直接关联被适配类
	private Adaptee adaptee;

	// 可以通过构造函数传入具体需要适配的被适配类对象
	public Adapter(Adaptee adaptee) {
		this.adaptee = adaptee;
	}

	public void request() {
		// 这里是使用委托的方式完成特殊功能
		this.adaptee.doSomething();
	}
}

//测试类
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 对象达到适配多个不同被适配类的功能,当然,此时我们可以为多个被适配类提取出一个接口或抽象类。这样看起来的话,似乎对象适配器模式更加灵活一点。

5.优点

  • 通过适配器,客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单、更直接、更紧凑。
  • 复用了现存的类,解决了现存类和复用环境要求不一致的问题。
  • 将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
  • 一个对象适配器可以把多个不同的适配者类适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
  • 灵活性好。如果某一天突然不想要适配器了,没问题,直接删除这个适配器就可以了,其他代码都不用修改。基本上就类似于一个灵活的构件,想用就用,不想用就卸载。

6.缺点

  • 类适配器的缺点:对于java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为接口,不能为类,其使用有一定的局限性,不能将一个适配者类和他的子类同时适配到目标接口
  • 对象适配器的缺点:与类适配器模式相比,要想置换适配者类的方法就不容易。

7.适用场景

  • 想要复用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。
  • 我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。
  • 使用第三方组件,组件接口定义和自己定义的不同,不希望修改自己的接口,但是要使用第三方组件接口的功能。