Java设计模式(三)适配器模式

1.什么是适配器

​ 适配器,这个概念挺好理解的,感觉就是个中介,两个不对付的物体A和B通过中介可以结合起来或者相适应,有点类似于居委会阿姨的感觉?哈哈哈,举个生活中的例子,iPhone推出新系列之后,圆头的耳机就无法插入iPhone手机了,必须得用扁平头的耳机,这个时候如果你不想再多花100rmb买个新耳机,那么你可以选择买一个转接头,将你的圆头耳机插入到转接口中,然后再将转接头插入到手机中,就可以实现圆头耳机插入到新系列iPhone了。再举个例子,美国电器 110V,中国 220V,将 110V 转化为 220V的这个工作我们就是交给适配器来完成的。

​ 现在想想是不是JAVA 中的 jdbc也是这么个道理呢。

2.面向对象的适配器

​ 假设Joe饲养了一群火鸡和鸭子。火鸡和鸭子都会叫而且也都会飞,只不过火鸡是咯咯叫,鸭子是呱呱叫,鸭子飞的比火鸡远很多。Joe时常将火鸡和鸭子给搞混了。

​ 由于鸭子和火鸡有很多不同的种类,在这儿给出简单的接口,并用一个具体的类来说明下各自的动作(比如飞),尽管后面我们不一定能用到下面示范的具体类。

public interface Duck {
	public void quack();//呱呱叫
	public void fly();//飞
}

MallardDuck:绿头鸭

public class MallardDuck implements Duck{

	@Override
	public void quack() {
		System.out.println("Quack");
		
	}

	@Override
	public void fly() {
		// TODO Auto-generated method stub
		System.out.println("I'm flying");
	}
}

Turkey:火鸡

public interface Turkey {
	public void gobble();
	public void fly();
}

WildTurkey:野生火鸡

public class WildTurkey implements Turkey{

	@Override
	public void gobble() {
		// TODO Auto-generated method stub
		System.out.println("Gobble");
		
	}

	@Override
	public void fly() {
		// TODO Auto-generated method stub
		System.out.println("I'm flying a short distance");
	}
	
}

​ 现在Joe需要很多鸭子,但是鸭子的数量无法满足需求,于是Joe打算用火鸡冒充鸭子,但是鸭子和火鸡终归是两个不同的物种,我们设计的接口也不同,肯定不能直接来用,这个时候我们就需要写个适配器来完成这个伪装过程。

public class TurkeyAdapter implements Duck{

	Turkey turkey;
	
	//既然用火鸡充当鸭子,显然在实现Duck接口方法的基础上,我们让本次伪装的对象参与进来
	//所以在构造方法中需要实例化Turkey
	public TurkeyAdapter(Turkey turkey) {
		this.turkey = turkey;
	}

	@Override
	public void quack() {
		// TODO Auto-generated method stub
		this.turkey.gobble();
		
	}

	@Override
	public void fly() {
		// TODO Auto-generated method stub
		for(int i=0;i<3;i++) {
			this.turkey.fly();
		}
	}	
}

​ 基本工作已经完成了,现在来测试一下。

public class DuckTestDrive {

	public static void main(String[] args) {
		//真正的鸭子--绿头鸭
		Duck duck=new MallardDuck();
		//真正的火鸡--野生火鸡
		Turkey turkey=new WildTurkey();
		//ok,现在是我们伪装出来的鸭子,实质上它还是个火鸡,我们的任务是做一点工作让它完成伪装的过程
		//我们通过刚刚new的TurkeyAdapter去完成这个过程的转换
		Duck fakeduck=new TurkeyAdapter(turkey);
		
		System.out.println("-------The Duck show time-------");
		duck.fly();
		duck.quack();
		System.out.println("-------The Turkey show time-------");
		turkey.fly();
		turkey.gobble();
		System.out.println("-------The FakeDuck(TurkeyAdapter) show time-------");
		fakeduck.fly();
		fakeduck.quack();

	}
}

打印结果:

-------The Duck show time-------
I'm flying
Quack
-------The Turkey show time-------
I'm flying a short distance
Gobble
-------The FakeDuck(TurkeyAdapter) show time-------
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
Gobble

3.过程分析

​ 1.客户(鸭子)通过目标接口(鸭子接口)调用适配器(TurkeyAdapter)的方法对适配器发出请求

​ 2.TurkeyAdapter适配器使用被适配者接口(Turkey)把请求转换成被适配者的一个或多个调用接口。

​ 3.客户(鸭子)收到调用的接口,但并未察觉到这一切是适配器在发挥作用。如果仔细想一想,其实我们可以发现,虽然我们用火鸡充当鸭子混在鸭子群体里,但是实际上鸭子根本不知道火鸡的存在(想想Duck和Adapter中Turkey的关系),所以客户和被适配者实际上是解耦的,因为一个不知道另一个。

适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本就扣不兼容的类可以合作无间。

​ 有关设计模式的书中对该模式定义如上。

​ 现在大概基本上了解了适配器,那我们再进一步的思考些问题:

​ 1.一个适配器只能封装一个类嘛?

​ 大多数的情况是这样的,适配器模式的定义刚刚我们说的是将一个类的接口,转换成客户期望的另一个接口。但显然生活中的例子怎么可能像刚才举的这么简单,所以一个适配器包装多个被适配者就涉及到另一种模式,即外观模式。但是注意,这两个模式不要混为一谈。

​ 2.有时候你可以让适配器实现两个接口,从而创建双向适配器,这样如果系统中新旧并存需要适配的话,或许更方便一些。

4.类适配器与对象适配器

​ 类适配器是继承被适配者和目标类,而对象适配器是使用组合来适配被适配者。之前我们举的例子是对象适配器的。

​ 还是以之前的鸭子火鸡为例,适配器Adapter要实现Duck接口,这样我们就能继续访问当前接口Duck中的方法(因为最终我们是把伪装好的火鸡当作鸭子来看,自然是要调用鸭子的方法和属性啦),然后再继承接口Turkey的实现类野生火鸡WildTurkey,这样我们可以在适配器Adapter中访问接口Turkey的方法了,这时我们在适配器Adapter中Duck接口的方法中直接引用WildTurkey中的合适方法,这样就完成了一个简单的类适配器。

类适配器:ObjectAdapter

public class ObjectAdapter extends WildTurkey implements Duck{

	@Override
	public void quack() {
		gobble();
	}
	@Override
	public void fly() {
		for(int i=0;i<3;i++)
		{
			super.fly();
		}
	}
}

测试方法:ObjectAdapterTest

public class ObjectAdapterTest {
	
	public static void main(String[] args) {
		System.out.println("----------类适配器-----------------");
		System.out.println("-----FakeDuck show time------");
		Duck duck=new ObjectAdapter();
		duck.quack();
		duck.fly();
	}
}

打印结果:

--------------类适配器------------
---------FakeDuck show time------
Gobble
I'm flying a short distance

类适配器模式注意事项和细节,以鸭子火鸡为例:

  1. Java是单继承机制,所以类适配器需要继承被适配者的具体类(类WildTurkey)这一点算是一个缺点, 因为这要求Duck(目标)必须是接口,有一定局限性(因为一个已经继承了,由于单继承,无法继承第二个,显然只能实现接口了)。

  2. 火鸡(本文中是WildTurkey)的方法在Adapter中都会暴露出来,也增加了使用的成本。

  3. 由于其继承了火鸡类,所以它可以根据需求重写火鸡类(本文中是WildTurkey)的方法,使得Adapter的灵活性增强了。

关于对象适配器:

  1. 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。 根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承被适配者(本文中是Turkey)的局限性问题,也不再要求目标(在本文中Duck)必须是接口。
  2. 使用成本更低,更灵活。
发布了58 篇原创文章 · 获赞 5 · 访问量 6285

猜你喜欢

转载自blog.csdn.net/weixin_40992982/article/details/103080124