设计模式之--适配器模式

设计模式--适配器模式


1、定义

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

2、模式中涉及的角色

目标接口:与客户接触的接口,换句话说,客户只会使用这个接口

被适配接口:需要转换成客户所期望的那样的接口

适配器:通过包装一个需要适配的接口,把该接口转换成目标接口


3、适配器的类型

类适配器:继承被适配者和目标类(多继承)来实现

对象适配器:使用组合来适配被适配者来实现

由此可见,类适配器是通过多重继承来实现的,而在java中这是不可能的,所以,这里只谈对象适配器


4、适配器模式(对象)类图

5、现实应用与举例

    有这么一个情景:一个客户拥有三台设备,分别是小米5、苹果手机、MP3,当他想为这三个设备充电时就得拥有三条对应的充电线,即各自的线的接口只适合自己用,不适合其他设备用。代码如下:

/**
 * 不用适配器的情况
 * @Author 先
 * @ClassName Client_NotAdapter.java
 * @Time 2017年3月11日 下午9:33:58
 */
public class Client_NotAdapter {
 
	public static void main(String[] args) {
		
		//给小米5充电,生成小米5专用充电线
		ElectryLine mi_line = new Xiaomi5();
		mi_line.electricize();
		
		//给苹果手机充电,生成苹果手机专用充电线
		ElectryLine apple_line = new Apple();
		apple_line.electricize();
		
		//给MP3充电,生成MP3专用充电线
		ElectryLine mp3_line = new MP3();
		mp3_line.electricize();
	}
}

/**
 * 充电线
 * @Author 先
 * @ClassName Client_Adapter.java
 * @Time 2017年3月11日 下午8:33:06
 */
interface ElectryLine{
	//充电方法
	void electricize();
}

/**
 * 小米5手机
 * @Author 先
 * @ClassName Client_Adapter.java
 * @Time 2017年3月11日 下午8:36:14
 */
class Xiaomi5 implements ElectryLine{

	@Override
	public void electricize() {
		System.out.println("小米5正在充电……");
		
	}
}

/**
 * 苹果手机
 * @Author 先
 * @ClassName Client_Adapter.java
 * @Time 2017年3月11日 下午8:37:15
 */
class Apple implements ElectryLine{

	@Override
	public void electricize() {
		System.out.println("苹果手机正在充电……");
		
	}
}

/**
 * MP3
 * @Author 先
 * @ClassName Client_Adapter.java
 * @Time 2017年3月11日 下午8:38:05
 */
class MP3 implements ElectryLine{
	
	@Override
	public void electricize() {
		System.out.println("MP3正在充电……");
		
	}
}

上述做法的缺点:

1、客户得创建三个对象(充电线),耗费内存空间多

2、没有将目标类与适配者类解耦

而使用适配器模式,就可以只用一条充电线,给三台不同接口的设备充电,就像这张图(网上随便找的一张)

这张图很好的解释了适配器的定义与应用,代码如下:

/**
 * 适配器模式
 * @Author 先
 * @ClassName Client_Adapter.java
 * @Time 2017年3月11日 下午8:26:52
 */

public class Client_Adapter {//客户

	public static void main(String[] args) {
		
		Xiaomi5 xiaomi = new Xiaomi5();
		Apple apple = new Apple();
		MP3 mp3 = new MP3();
		
		//这句话可以这么理解:充电线的引用指向了适配器,相当于将充电线插到转接头上
		ElectryLine line = new Adapter(xiaomi,apple,mp3);
		
		//如果客户想给小米5充电
		line.electricize("xiaomi5");
		
		//如果客户想给苹果手机充电
		line.electricize("apple");
		
		//如果客户想给MP3充电
		line.electricize("mp3");
		
		
		//总结分析:整个过程,客户只创建了一个充电线的对象,却通过了适配器(即转接头)给三台不同的设备充电了
		//实际上,这个适配器只是把一个接口转换成另一个接口了(并没有同时转换成三个接口),
		//只不过是通过传参的形式来判断到底要转换成哪个接口,转换的只是小米5或者苹果或者MP3
		//本里这样子写只是为了让读者更好地理解适配器
	}
}

/**
 * 充电线
 * @Author 先
 * @ClassName Client_Adapter.java
 * @Time 2017年3月11日 下午8:33:06
 */
interface ElectryLine{
	//充电方法
	void electricize(String type);
}

/**
 * 小米5手机
 * @Author 先
 * @ClassName Client_Adapter.java
 * @Time 2017年3月11日 下午8:36:14
 */
class Xiaomi5{
	public void mi_electry(){
		System.out.println("小米5正在充电……");
	}
}

/**
 * 苹果手机
 * @Author 先
 * @ClassName Client_Adapter.java
 * @Time 2017年3月11日 下午8:37:15
 */
class Apple{
	public void apple_electry(){
		System.out.println("苹果手机正在充电……");
	}
}

/**
 * MP3
 * @Author 先
 * @ClassName Client_Adapter.java
 * @Time 2017年3月11日 下午8:38:05
 */
class MP3{
	public void mp3_electry(){
		System.out.println("MP3正在充电……");
	}
}

/**
 * 这里假设这个适配器就只是转接小米5、苹果和MP3的
 * @Author 先
 * @ClassName Client_Adapter.java
 * @Time 2017年3月11日 下午8:40:15
 */
class Adapter implements ElectryLine{

	//对象适配器使用的是组合语法
	Xiaomi5 xiaomi;
	Apple apple;
	MP3 mp3;
	
	//用构造器取得被适配对象的引用
	public Adapter(Xiaomi5 xiaomi,Apple apple,MP3 mp3){
		this.xiaomi = xiaomi;
		this.apple = apple;
		this.mp3 = mp3;
	}
	
	@Override
	public void electricize(String type) {
		switch(type){
		case "xiaomi5" : xiaomi.mi_electry();break;
		case "apple" : apple.apple_electry();break;
		case "mp3" : mp3.mp3_electry();break;
		}
		
	}
	
}
分析:

看起来使用了适配器模式后代码变多了,但是将目标类与适配者类分开了,即将充电线与手机分开,客户指操作一条线,中间通过转接口(适配器)来给不同的设备充电,对于客户来说,体验好了,从内存的角度来说,也节省了空间。正如《Thinking in java》中所说--“适配器接受你任意的接口类型,并且产生你需要的接口类型”,这里就验证的这句话,转接头(适配器)接受不同的设备接口,生成客户想要的那个接口(目标接口ElectryLine)。

再用代码来解释本文上述的模型图:

/**
 * 目标接口
 * @Author 先
 * @ClassName Client_NotAdapter.java
 * @Time 2017年3月11日 下午11:18:35
 */
interface Target{
	void request();
}

/**
 * 被适配类
 * @Author 先
 * @ClassName Client_NotAdapter.java
 * @Time 2017年3月11日 下午11:19:53
 */
class Adaptee{
	public void specificRequest(){}
}

/**
 * 适配器
 * @Author 先
 * @ClassName Client_NotAdapter.java
 * @Time 2017年3月11日 下午11:21:19
 */
class Adapter implements Target{
	Adaptee adaptee;
	
	public Adapter(Adaptee adaptee){
		this.adaptee = adaptee;
	}

	@Override
	public void request() {
		adaptee.specificRequest();
		
	}
}

6、总结:

优点:

1、通过适配器,客户可以调用同一个接口,来操作本来接口不匹配的对象,而这些实现对客户是透明的。

2、复用了现存的类,解决了现存类和复用环境要求不一致的问题。

3、将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
4、一个对象适配器可以把多个不同的适配者类适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。


总而言之,就是

更好的复用性

系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。

更好的扩展性

在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。


缺点:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yeqiu1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值