适配器模式
本文主要参考资料:《设计模式之禅》
本文主要目录为:
1)案例
2)适配器模式解决
3)适配器模式的定义
4)拓展
1. 案例
比如我们现在有一个人力资源管理系统,其中人员信息管理的对象是所有员工的所有信息,这里的所有员工是指在职的员工,其他的离职的,退修的暂时不考虑。类图与代码如下:
public interface IUserInfo {
//获得用户姓名
public String getUserName();
//获得家庭地址
public String getHomeAddress();
//手机号码,这个太重要,手机泛滥呀
public String getMobileNumber();
//办公电话,一般式座机
public String getOfficeTelNumber();
//这个人的职位是啥
public String getJobPosition();
//获得家庭电话,这个有点缺德,我是不喜欢打家庭电话讨论工作
public String getHomeTelNumber();
}
public class UserInfo implements IUserInfo {
/*
* 获得家庭地址,下属送礼也可以找到地方
*/
public String getHomeAddress() {
System.out.println("这里是员工的家庭地址....");
return null;
}
/*
* 获得家庭电话号码
*/
public String getHomeTelNumber() {
System.out.println("员工的家庭电话是....");
return null;
}
/*
* 员工的职位,是部门经理还是小兵
*/
public String getJobPosition() {
System.out.println("这个人的职位是BOSS....");
return null;
}
/*
* 手机号码
*/
public String getMobileNumber() {
System.out.println("这个人的手机号码是0000....");
return null;
}
/*
* 办公室电话,烦躁的时候最好“不小心”把电话线踢掉,我经常这么干,对己对人都有好处
*/
public String getOfficeTelNumber() {
System.out.println("办公室电话是....");
return null;
}
/*
* 姓名了,这个老重要了
*/
public String getUserName() {
System.out.println("姓名叫做...");
return null;
}
}
但是过了几年,人力资源说需要增加一个功能:借用人员管理。
借用人员虽然在我们公司干活,但是他们的人员信息,工资请款等都是由劳动服务公司管理,并且有一套自己的人员管理系统。所以我们需要将人资系统同步劳动服务公司的信息
劳动服务公司是把人员信息分为三部分:基本信息,办公信息和个人家庭信息,并且都放在HashMap中,类图和代码如下所示:
@SuppressWarnings("all")
public interface IOuterUser {
//基本信息,比如名称,性别,手机号码了等
public Map getUserBaseInfo();
//工作区域信息
public Map getUserOfficeInfo();
//用户的家庭信息
public Map getUserHomeInfo();
}
@SuppressWarnings("all")
public class OuterUser implements IOuterUser {
/*
* 用户的基本信息
*/
public Map getUserBaseInfo() {
HashMap baseInfoMap = new HashMap();
baseInfoMap.put("userName", "这个员工叫混世魔王....");
baseInfoMap.put("mobileNumber", "这个员工电话是....");
return baseInfoMap;
}
/*
* 员工的家庭信息
*/
public Map getUserHomeInfo() {
HashMap homeInfo = new HashMap();
homeInfo.put("homeTelNumbner", "员工的家庭电话是....");
homeInfo.put("homeAddress", "员工的家庭地址是....");
return homeInfo;
}
/*
* 员工的工作信息,比如职位了等
*/
public Map getUserOfficeInfo() {
HashMap officeInfo = new HashMap();
officeInfo.put("jobPosition","这个人的职位是BOSS...");
officeInfo.put("officeTelNumber", "员工的办公电话是....");
return officeInfo;
}
}
2. 适配器模式解决
现在问题来了,咱们的系统要和他们的系统进行交互,怎么办?我们不可能为了这一小小的功能而对我们已经运行良好的系统大动手术,那怎么办?我们可以转化,先拿到对方的数据对象,然后转化为我们自己的数据对象,中间加一层转换处理:按照这个思路,我们可以设计如下类图:
OuterUserInfo可以看做是“两面派”,实现了IUserInfo接口,还继承了OuterUser,通过这样的设计,把OuterUser伪装成我们系统中一个IUserInfo对象,这样,我们系统基本不用修改,所有的人员查询,调用和本地一样。(我们之所以能够增加一个OuterUserInfo中转类,是因为我们再系统设计时严格遵守了依赖倒置原则和里式 替换原则,否则即使增加中转类也无法解决问题)
中转角色OuterUserInfo代码清单:
@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 APP {
public static void main(String[] args) {
//没有与外系统连接的时候,是这样写的
//IUserInfo youngGirl = new UserInfo();
//老板一想不对呀,兔子不吃窝边草,还是找人力资源的员工好点
IUserInfo youngGirl = new OuterUserInfo(); //我们只修改了这一句好
//从数据库中查到101个
for(int i=0;i<101;i++){
youngGirl.getMobileNumber();
}
}
}
大家可以看到,使用了适配器模式只修改了一句话,其他业务逻辑都不用修改就解决了系统对接文,而且在我们实际系统中只增加了一个业务类的继承,就实现了查本公司的员工信息,也可以查人力资源公司的员工信息,尽量少的修改,通过扩展的方式解决了该问题,这就是适配模式。
3. 适配器模式的定义
定义:Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.(将一个类的接口变换成客户端所期待的另一种接口,从而是原本因接口不匹配而无法在一起工作的两个类能够在一起工作。)
我们先来看看适配器模式的三个角色:
- Target目标角色:该角色定义把其它类转换为何种接口,也就是我们的期望接口。
- 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();
}
}
适配器模式的应用
1.适配器模式的优点
- 适配器模式可以让两个没有任何关系得类在一起运行,只要适配器这个角色能够搞定他就成。
- 增加了类的透明性。我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块时透明的,也是它不需要关心的。
- 提高了类的复用度。
- 灵活性非常好
2.适配器模式的使用场景
适配器应用的场景只要记住一点就足够了:你有动机修改了一个已经投产中的接口时,适配器模式可能是最适合你的模式。
3.适配器模式的注意事项
适配器模式最好在详细设计不要考虑它,它不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题。
4. 适配器模式的拓展
上面那个例子是个比较幸运的例子。如果劳动服务公司提供的接口不止一个,也就是说,用户基本信息是一个接口,工作信息是一个接口,家庭信息是一个接口,总共有三个接口三个实现类呢?
我们不能在使用上面的方法,因为java是不支持多继承的,你难道想让OuterUserInfo继承三个实现类.?
所以我们可以使用类关联:申明一个OuterUserInfo实现类,实现IUserInfo接口,再通过关联其他三个实现类就可以解决了,我们来看下类图和OuterUserInfo类
@SuppressWarnings("all")
public class OuterUserInfo implements IUserInfo {
//源目标对象
private IOuterUserBaseInfo baseInfo = null; //员工的基本信息
private IOuterUserHomeInfo homeInfo = null; //员工的家庭 信息
private IOuterUserOfficeInfo officeInfo = null; //工作信息
//数据处理
private Map baseMap = null;
private Map homeMap = null;
private Map officeMap = null;
//构造函数传递对象
public OuterUserInfo(IOuterUserBaseInfo _baseInfo,IOuterUserHomeInfo _homeInfo,IOuterUserOfficeInfo _officeInfo){
this.baseInfo = _baseInfo;
this.homeInfo = _homeInfo;
this.officeInfo = _officeInfo;
//数据处理
this.baseMap = this.baseInfo.getUserBaseInfo();
this.homeMap = this.homeInfo.getUserHomeInfo();
this.officeMap = this.officeInfo.getUserOfficeInfo();
}
//家庭地址
public String getHomeAddress() {
String homeAddress = (String)this.homeMap.get("homeAddress");
System.out.println(homeAddress);
return homeAddress;
}
//家庭电话号码
public String getHomeTelNumber() {
String homeTelNumber = (String)this.homeMap.get("homeTelNumber");
System.out.println(homeTelNumber);
return homeTelNumber;
}
//职位信息
public String getJobPosition() {
String jobPosition = (String)this.officeMap.get("jobPosition");
System.out.println(jobPosition);
return jobPosition;
}
//手机号码
public String getMobileNumber() {
String mobileNumber = (String)this.baseMap.get("mobileNumber");
System.out.println(mobileNumber);
return mobileNumber;
}
//办公电话
public String getOfficeTelNumber() {
String officeTelNumber = (String)this.officeMap.get("officeTelNumber");
System.out.println(officeTelNumber);
return officeTelNumber;
}
// 员工的名称
public String getUserName() {
String userName = (String)this.baseMap.get("userName");
System.out.println(userName);
return userName;
}
}
相关阅读
原文:http://www.cnblogs.com/zhangchenliang/p/3700820.html 结合简单示例和UML图,讲解工厂模式简单原理。 一、引子 话说十年前,
UltraEdit如何使用列模式?Ultra Edit一款功能强大的文本编辑器,可以编辑文本、十六进制、ASCII 码,可以取代记事本,内建英文单字检查
民营快递在中航协的处罚下似乎现了原形。作为“淘宝系”依靠的社会化物流体系的中坚力量,圆通、韵达两家快递企业因违规操作日前被
很多新手朋友认为,淘宝客就是在群里面发产品,别人买了可以赚取佣金,其实这很片面,淘宝客的推广赚钱模式还有很多,下面就给大家说一说淘
酷影模式是UC浏览器将PC网站移动化的一种网页转码(或称网页重排)模式之一,属于UC浏览器独家、特色功能。核心特点1、适合移动设备