适配器模式:
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作了。
适配器模式结构图
Target
这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口
package adapter;
/**
* Author: ydc
* Date: 2021-1-21
* Description: 这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口
*/
public class Target {
/**
* client端只想用普通接口Request
*/
public void Request() {
System.out.println("普通请求");
}
}
Adaptee
可以理解为早期实现的功能代码,早期对外提供一些有价值的接口服务,可能现在的新系统,新环境也需要某些部分的旧接口服务,但由于早期是为了满足早期的场景定义好了对外接口。 并且这些旧接口有可能好多其他系统 在使用,所以无法在短期内改造接口。而且新系统或新环境,已根据当前的场景定义好了相关接口。所以需要一种手段去解决这种问题。
package adapter;
/**
* Author: ydc
* Date: 2021-1-21
* Description: 需要适配的类
* 可以理解为早期实现的功能代码,早期对外提供一些有价值的接口服务,可能现在的新系统,新环境也需要某些部分的旧接口服务,但由于早期是为了满足早期的场景定义好了对外接口。
* 并且这些旧接口有可能好多其他系统
* 在使用,所以无法在短期内改造接口。而且新系统或新环境,已根据当前的场景定义好了相关接口。所以需要一种手段去解决这种问题。
*
*/
public class Adaptee {
public void SpecificRequest() {
System.out.println("特殊请求");
}
}
Adapter
适配器类:通过再内部包装一个要适配的类(Adaptee),把源接口转换成目标接口
package adapter;
/**
* Author: ydc
* Date: 2021-1-21
* Description: 适配器类:通过再内部包装一个要适配的类(Adaptee),把源接口转换成目标接口
*/
public class Adapter extends Target {
private Adaptee adaptee = new Adaptee();
/**
* 目标接口
*/
@Override
public void Request() {
adaptee.SpecificRequest();/**源接口或旧接口**/
}
}
Client
Target target = new Adapter();
target.Request();
具体案例
翻译适配器(姚明早期在NBA如何打球?)
package adapter.demo;
/**
* Author: ydc
* Date: 2021-1-21
* Description: 这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口
* 篮球运动员
*/
public abstract class Player {
protected String name;
public Player(String name) {
this.name = name;
}
public abstract void Attack();
public abstract void Defense();
}
package adapter.demo;
/**
* Author: ydc
* Date: 2021-1-21
* Description:符合客户所期待的接口
* 前锋
*/
public class Forwards extends Player {
public Forwards(String name) {
super(name);
}
@Override
public void Attack() {
System.out.println("前锋" + name + "进攻");
}
@Override
public void Defense() {
System.out.println("前锋 " + name + " 防守");
}
}
package adapter.demo;
/**
* Author: ydc
* Date: 2021-1-21
* Description:符合客户所期待的接口
* 中锋
*/
public class Center extends Player {
public Center(String name) {
super(name);
}
@Override
public void Attack() {
System.out.println("中锋" + name + "进攻");
}
@Override
public void Defense() {
System.out.println("中锋 " + name + " 防守");
}
}
package adapter.demo;
/**
* Author: ydc
* Date: 2021-1-21
* Description:因为外籍中锋刚到美国打球时,虽然球技一流,但是不懂英语,
* 无法与教练及其他非外籍球员沟通,所以需要一个翻译,
* 所以它不符合户所期待的接口,所以不能继承目标接口。它是一个待适配的类
*
* 外籍中锋
*/
public class ForeignCenter {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
public void 进攻() {
System.out.println("外籍中锋" + name + "进攻");
}
public void 防守() {
System.out.println("外籍中锋 " + name + " 防守");
}
}
package adapter.demo;
/**
* Author: ydc
* Date: 2021-1-21
* Description:符合客户所期待的接口
* 后卫
*/
public class Guards extends Player {
public Guards(String name) {
super(name);
}
@Override
public void Attack() {
System.out.println("后卫" + name + "进攻");
}
@Override
public void Defense() {
System.out.println("后卫 " + name + " 防守");
}
}
package adapter.demo;
/**
* Author: ydc
* Date: 2021-1-21
* Description: 篮球翻译适配器,通过它可以把教练及非外籍球员的意图转达给外籍中锋,
* 这样就达到了,虽然外籍中锋不懂英语,也可以在NBA展现他们的打球天赋.
* 篮球翻译适配器能够使得不懂中文的NBA教练和不懂英语的外籍球员一起工作
*/
public class Translator extends Player {
private ForeignCenter wjzf = new ForeignCenter();
public Translator(String name) {
super(name);
wjzf.setName(name);
}
@Override
public void Attack() {
wjzf.进攻();
}
@Override
public void Defense() {
wjzf.防守();
}
}
Client
Player b = new Forwards("巴蒂尔");
b.Attack();
Player m = new Guards("麦克格雷迪");
m.Attack();
//Player ym = new Center("姚明");
Player ym = new Translator("姚明");
ym.Attack();
ym.Defense();
篮球翻译适配器结构图
总结:
- 姚明虽然球技一流,但是刚到NBA时,短期内,让他达到与教练及其他球员使用英语无障碍沟通是不现实的,同时让教练及其他球员学习中文来沟通也是不现实的事情,在双方都无法改造的情况下,找个翻译来解决沟通问题是个很好的方案,这个翻译就是适配器。所以在双方都不太容易修改的时候再使用适配器模式。
2.适配器模式对早期代码复用一些功能,很有价值。在想使用一个已经存在的类,但如果它的接口,也就是它的方法和你的要求不相同时,就应该考虑使用适配器。
3.其实是适配器Adapter类这一层,对现有类的接口,做一些处理之后或者直接转发源接口,能够输出满足Client的数据或行为要求。客户端能够通过适配器来统一调用同一个接口,不需要关心源(旧)接口的细节。
应用
1.在java中早期的枚举接口是Enumeration而后定义的枚举接口是Iterator;有很多旧的类实现了Enumeration接口暴露出了一些服务,但是这些服务我们现在想通过传入Iterator接口而不是Enumeration接口来调用,这时就需要一个适配器,那么client就能用这个服务了(client端只想用Iterator或者只知道这个接口)。
2…NET中使用DataAdapter把SQL Server、Oracle等数据源转换成DataSet所需要的数据格式统一输出给Client,这样Client就不必关心不同数据库的数据细节。