定义:Convert the interface of a class into another interface clients expect.Adapter lets classes worktogether that couldn’t otherwise because of incompatible interfaces.(将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。)
应用场景:
美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。
JavaJDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。
在 LINUX 上运行 WINDOWS 程序。
Java中的 jdbc。
角色介绍
Target :目标角色该角色定义把其他类转换为何种接口,也就是我们的期望接口,例子中的IUserInfo接口就是目标角色。
Adaptee:源角色你想把谁转换成目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象,经过适配器角色的包装,它会成为一个崭新、靓丽的角色。
Adapter:适配器角色适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:把源角色转换为目标角色,怎么转换?通过继承或是类关联的方式。
通用代码清单
public interface Target {
//目标角色有自己的方法
public void request();
}
public class ConcreteTarget implements Target {
public void request() {
System.out.println("I have nothing to do. if you need any help,pls call me!"); }
}
public class Adaptee {
//原有的业务逻辑
public void doSomething(){
System.out.println("I'm kind of busy,leave me alone,pls!");
}
}
public 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 target2 = new Adapter();
target2.request();
}
}
案例
已经有一个人力资源管理项目,分为三大模块:人员信息管理、薪酬管理、职位管理。根据需求设计了如图19-1所示的类图。
代码如下:
public interface IUserInfo {
String getUserName();
String getHomeAddress();
String getMobileNumber();
String getOfficeTelNumber();
String getJobPosition();
}
public class UserInfo implements IUserInfo{
@Override
public String getUserName() {
return "userName";
}
@Override
public String getHomeAddress() {
return "homeAddress";
}
@Override
public String getMobileNumber() {
return "mobile";
}
@Override
public String getOfficeTelNumber() {
return "telNumber";
}
@Override
public String getJobPosition() {
return "jobPosition";
}
}
现如今需求变动,公司为节省成本,从劳动资源公司借用了一批人员分配到子公司,要新增一个借用人员管理的模块功能。因此我们需要从劳务公司提供的人员信息进行查询。类图如19-2所示。
接口代码如下:
//基本信息,比如名称、性别、手机号码等
Map getUserBaseInfo();
//工作区域信息
Map getUserOfficeInfo();
//用户的家庭信息
Map getUserHomeInfo();
实现代码如下:
public class OuterUser implements IOuterUser{
/*
* 用户的基本信息
*/
@Override
public Map getUserBaseInfo() {
HashMap baseInfoMap = new HashMap();
baseInfoMap.put("userName", "这个员工叫混世魔王...");
baseInfoMap.put("mobileNumber", "这个员工电话是...");
return baseInfoMap;
}
/*
* 员工的家庭信息
*/
@Override
public Map getUserHomeInfo() {
HashMap homeInfo = new HashMap();
homeInfo.put("homeTelNumbner", "员工的家庭电话是...");
homeInfo.put("homeAddress", "员工的家庭地址是...");
return homeInfo;
}
/*
* 员工的工作信息,比如,职位等
*/
@Override
public Map getUserOfficeInfo() {
HashMap officeInfo = new HashMap();
officeInfo.put("jobPosition","这个人的职位是BOSS...");
officeInfo.put("officeTelNumber", "员工的办公电话是...");
return officeInfo;
}
}
现在我们需要从劳务公司现有的代码结构来转换为我们需要的接口,于是设计了如图所示19-3的类图。
OuterUserInfo可以看做是“两面派”,实现了IUserInfo接口,还继承了OuterUser,通过这样的设计,把OuterUser伪装我们系统中一个IUserInfo对象,这样,我们的系统基本不用修改,所有的人员查询、调用跟本地一样。
@SuppressWarnings("all")
public class OuterUserInfo extends OuterUser implements IUserInfo {
private Map baseInfo = super.getUserBaseInfo(); //员工的基本信息
private Map homeInfo = super.getUserHomeInfo(); //员工的家庭 信息
private Map officeInfo = super.getUserOfficeInfo(); //工作信息
/*
* 家庭地址
*/
public String getHomeAddress() {
String homeAddress = (String)this.homeInfo.get("homeAddress");
System.out.println(homeAddress);
return homeAddress;
}
/*
* 家庭电话号码
*/
public String getHomeTelNumber() {
String homeTelNumber = (String)this.homeInfo.get("homeTelNumber");
System.out.println(homeTelNumber);
return homeTelNumber;
}
/*
*职位信息
*/
public String getJobPosition() {
String jobPosition = (String)this.officeInfo.get("jobPosition");
System.out.println(jobPosition);
return jobPosition;
}
/*
* 手机号码
*/
public String getMobileNumber() {
String mobileNumber = (String)this.baseInfo.get("mobileNumber");
System.out.println(mobileNumber);
return mobileNumber;
}
/*
* 办公电话
*/
public String getOfficeTelNumber() {
String officeTelNumber = (String)this.officeInfo.get("officeTelNumber");
System.out.println(officeTelNumber);
return officeTelNumber;
}
/*
* 员工的名称
*/
public String getUserName() {
String userName = (String)this.baseInfo.get("userName");
System.out.println(userName);
return userName;
}
}
方法调用
public class Main {
public static void main(String[] args) {
IUserInfo outerUserInfo=new OuterUserInfo();
outerUserInfo.getHomeAddress();
outerUserInfo.getUserName();
}
}
使用了适配器模式只修改了一句话,其他的业务逻辑都不用修改就解决了系统对接的问题,而且在我们实际系统中只是增加了一个业务类的继承,就实现了可以查本公司的员工信息,也可以查人力资源公司的员工信息,尽量少的修改,通过扩展的方式解决了该问题。这就是适配模式。
总结
优点:
-
适配器模式可以让两个没有任何关系得类在一起运行,只要适配器这个角色能够搞定他就成。
-
增加了类的透明性。我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块时透明的,也是它不需要关心的。
-
提高了类的复用度。
-
灵活性非常好
注意事项:
适配器模式最好在详细设计不要考虑它,它不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题。
项目一定要遵循依赖倒置原则和里氏替换原则,否则在使用适配器模式下,也会带来非常大的改造。
代码已上传到github: 适配器模式demo
参考资料
《设计模式之禅》