本文来自李明子csdn博客(http://blog.csdn.net/free1985),商业转载请联系博主获得授权,非商业转载请注明出处!
1 目的
适配器(Adapter)提供了将一个接口转换为另一个接口的方法。
2 基本形态
适配器(基于对象)的基本形态如类图2-1所示。
图2-1 适配器(基于对象)类图
3 参与者
结合图2-1,下面介绍各类在适配器(基于对象)设计模式中扮演的角色。
3.1 Target
Target是目标接口,即希望转换成的接口,其中声明了需要实现的各接口方法。
3.2 Adaptee
Adaptee是原接口。这里所说的“接口”是广义的接口,实际上,Adaptee为包含各方法实现的具体类。一般来说,Adaptee基本包含了目标接口需要的功能,但方法的名称、参数、返回值可能不同。
3.3 Adapter
Adapter是适配器,它实现了Target接口并实例化Adaptee作为成员变量或临时变量。Adapter在实现Target各接口方法时通过重组参数、返回值的形式调用了实例化的Adaptee的相同业务功能的方法。
3.4 Client
Client是适配器设计模式的使用者。它使用Adapter类实例化Target接口,进而调用Target中声明的各方法实现业务功能。
4 代码实践
下面我们用一个业务场景实例来进一步讲解适配器的使用。
4.1 场景介绍
某OA系统可与不同的HR系统集成,用户信息由HR系统维护,用户的组织结构关系由OA系统维护。
以下各节将介绍该场景各类的具体实现及其在适配器设计模式中所对应的参与者角色。
4.2 IUserMgmt
IUserMgmt是用户管理接口,声明了“添加用户”等用户操作方法。对应于适配器模式的参与者,IUserMgmt是我们的目标接口Target。下面的代码给出了IUserMgmt的声明。
package demo.designpattern.adapter;
/**
* 目标用户管理接口
* Created by LiMingzi on 2017/7/6.
*/
public interface IUserMgmt {
/**
* 添加用户
* @param name 用户名
* @return 新用户id
* @throws RuntimeException 添加失败,message为失败原因
*/
public String addUser(String name) throws RuntimeException;
}
4.3 UserOperation
UserOperation是HR(原系统)的用户操作类,声明了用户操作的各方法。对应于适配器模式的参与者,UserOperation是我们的原接口Adaptee。下面的代码给出了UserOperation的声明。
package demo.designpattern.adapter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* 原系统用户操作类
* Created by LiMingzi on 2017/7/6.
*/
public class UserOperation {
/**
* 用户字典,key为用户名,value为用户流水
*/
private Map<String,Integer> userMap;
/**
* 构造方法
*/
public UserOperation() {
initUserMap();
}
/**
* 初始化用户字典,演示示例
*/
private void initUserMap(){
userMap=new HashMap<String, Integer>();
userMap.put("张三",1);
}
/**
* 插入用户
* @param name 用户名
* @return 插入是否成功
*/
public boolean insertUser(String name){
// 操作结果
boolean operation;
try {
userMap.put(name, getMaxUserSn()+1);
operation=true;
}catch (Exception ex){
operation=false;
}
return operation;
}
/**
* 获取最大流水
* @return 最大流水号
*/
private int getMaxUserSn(){
return Collections.max(userMap.values());
}
/**
* 获取用户流水码
* @param name 用户名
* @return 流水码,为0时表示用户不存在
*/
public int getUserSn(String name){
// 用户流水码
int sn = 0;
if(userMap.containsKey(name)){
sn = userMap.get(name);
}
return sn;
}
}
上述代码中,15行声明了该类以成员变量形式维护的用户集合,其中包含用户名及用户流水。27行,初始化用户字典方法为演示方法,添加了默认用户。实际代码中应为从数据库等持久层载入用户数据。37行插入用户的实现也与此类似,仅为演示使用。
4.4 UserOperationAdapter
UserOperationAdapter类是用户操作适配器类,主要声明了添加用户的方法addUser。UserOperationAdapter类实现了IUserMgmt接口,并声明了UserOperation类型的成员变量。对应于适配器模式的参与者,UserOperationAdapter是我们的适配器Adapter。下面的代码给出了UserOperationAdapter的声明。
package demo.designpattern.adapter.forobject;
import demo.designpattern.adapter.IUserMgmt;
import demo.designpattern.adapter.UserOperation;
/**
* 用户操作适配器
* Created by LiMingzi on 2017/7/6.
*/
public class UserOperationAdapter implements IUserMgmt {
/**
* 用户操作对象
*/
private UserOperation userOperation = new UserOperation();
/**
* 添加用户
*
* @param name 用户名
* @return 新用户id
* @throws RuntimeException 添加失败,message为失败原因
*/
@Override
public String addUser(String name) throws RuntimeException {
// 用户流水
int userSn = userOperation.getUserSn(name);
// 用户已存在
if(userSn>0){
throw new RuntimeException("用户已存在,添加用户失败");
}
if(!userOperation.insertUser(name)){
throw new RuntimeException("未知错误,添加用户失败");
}
userSn = userOperation.getUserSn(name);
return Integer.toString(userSn);
}
}
上述代码中,24行实现了IUserMgmt 接口的addUser方法。26行调用UserOperation实例的方法getUserSn获取了用户的流水号,28行通过流水号是否大于0判断用户是否已经存在。我们可以看到,通过adapter的包装,原接口的添加用户方法已被适配成满足目标接口的方法。本例中,不但方法名称、返回值、异常等元素被转换,内部判断逻辑也做了少许改动。
4.5 UserRegister
UserRegister是用户注册类,实现了用户注册功能。对应于适配器模式的参与者,UserRegister是Client。下面的代码给出了UserRegister的声明。
package demo.designpattern.adapter;
import demo.designpattern.adapter.IUserMgmt;
/**
* 用户注册类
* Created by LiMingzi on 2017/7/6.
*/
public class UserRegister {
/**
* 构造方法
* @param userMgmt 用户管理对象
*/
public UserRegister(IUserMgmt userMgmt) {
this.userMgmt = userMgmt;
}
/**
* 用户管理对象
*/
private IUserMgmt userMgmt;
/**
* 注册新用户
* @param userName 用户名
* @param orgName 组织名
*/
public void registerUser(String userName, String orgName){
try{
// 用户id
String userId = userMgmt.addUser(userName);
addUser2Org(userId,orgName);
}catch (RuntimeException ex){
System.out.println("用户\""+userName+"\"注册失败:"+ex.getMessage()+";");
return;
}
System.out.println("用户\""+userName+"\"注册成功;");
}
/**
* 将用户添加到组织(demo)
* @param userId 用户id
* @param orgName 组织名
*/
private void addUser2Org(String userId,String orgName){
System.out.println("ID为\""+userId+"\"的用户被添加到组织\""+orgName+"\"中;");
}
}
上述代码中,14行,通过有参构造方法指定用户管理对象接口的实现类实例。26行,声明了主要的业务方法注册用户,其逻辑是,添加用户并获取用户id(29行),将用户注册到指定的组织(30行)。43行将用户添加到组织为演示方法,这里仅做了用户注册到组织的信息输出。
4.6 测试代码
为了测试本文中的代码,我们可以编写如下测试代码。
/**
* 适配器(基于对象)测试
*/
public static void adapterForObjectTest(){
// 用户管理对象
IUserMgmt userMgmt = new demo.designpattern.adapter.forobject.UserOperationAdapter();
// 用户注册类对象
UserRegister userRegister = new UserRegister(userMgmt);
userRegister.registerUser("张三","销售部");
System.out.println("-----------------------------------------------------------------------------");
userRegister.registerUser("李四","采购部");
}
编译运行后,得到如下测试结果:
用户”张三”注册失败:用户已存在,添加用户失败;
—————————————————————————–
ID为”2”的用户被添加到组织”采购部”中;
用户”李四”注册成功;
5 扩展
5.1 基于类与基于对象实现的辨析
在上一讲和本讲中我们介绍了适配器模式的两种实现形式——基于类和基于对象。那么我们在实际工作中应该使用哪种方式呢?答案是要视具体场景而定。为了便于大家选择,我们在这里总结一下基于类与基于对象实现的特点:
- 基于类的实现代码行更少,实现更简单;
- 基于对象的实现代码更为灵活,适配器与原接口耦合更松散;
- 基于类的实现在不做处理的情况下适配器可能对外暴露不必要的信息;
- 基于类的实现更自然的支持被适配接口;
- 对于适配器的一个变种——多Adaptee(多个原接口的功能实现一个适配目标),在不支持多继承的java语言中,使用基于对象的实现是唯一的选择;