19.4 适配器模式的扩展

19.4 适配器模式的扩展

我们刚刚讲的人力资源管理的例子中,其实是一个比较幸运的例子,为什么呢?如果劳动服务公司提供的人员接口不止一个,也就是说,用户基本信息是一个接口,工作信息是一个接口,家庭信息是一个接口,总共有三个接口三个实现类,想想看如何处理呢?不能再使用我们上面的方法了,为什么呢?Java是不支持多继承的,你难道想让OuterUserInfo继承三个实现类?此路不通,再想一个办法,对哦,可以使用类关联的办法嘛!声明一个OuterUserInfo实现类,实现IUserInfo接口,通过再关联其他三个实现类不就可以解决这个问题了吗?是的,是的,好方法,我们先画出类图,如图19-8所示。

OuterUserInfo通过关联的方式与外界的三个实现类通讯,当然也可以理解为是聚合关系。IUserInfo和UserInfo代码如代码清单19-1和代码清单19-2所示,不再赘述。我们来看看拆分后的三个接口和实现类,用户基本信息接口如代码清单19-13所示。

image-20210929163847652

图19-8 拆分接口后的类图

代码清单19-13 用户基本信息接口

1
2
3
4
public interface IOuterUserBaseInfo {
//基本信息,比如名称、性别、手机号码等
public Map getUserBaseInfo();
}

用户家庭信息接口如代码清单19-14所示。

代码清单19-14 用户家庭信息接口

1
2
3
4
5
public interface IOuterUserHomeInfo {
//用户的家庭信息
public Map getUserHomeInfo();

}

用户工作信息接口如代码清单19-15所示。

代码清单19-15 用户工作信息接口

1
2
3
4
public interface IOuterUserOfficeInfo {
//工作区域信息
public Map getUserOfficeInfo();
}

读到这里,读者应该想到这样一个问题:系统这样设计是否合理呢?合理,绝对合理! 想想单一职责原则是怎么说的,类和接口要保持职责单一,在实际的应用中类可以有多重职责,但是接口一定要职责单一,因此,我们上面拆分接口的假想也是非常合乎逻辑的。我们来看三个相关的实现类,用户基本信息如代码清单19-16所示。

代码清单19-16 用户基本信息

1
2
3
4
5
6
7
8
9
10
11
public class OuterUserBaseInfo implements IOuterUserBaseInfo {
/*
* 用户的基本信息
*/
public Map getUserBaseInfo() {
HashMap baseInfoMap = new HashMap();
baseInfoMap.put("userName", "这个员工叫混世魔王...");
baseInfoMap.put("mobileNumber", "这个员工电话是...");
return baseInfoMap;
}
}

用户家庭信息如代码清单19-17所示。

代码清单19-17 用户家庭信息

1
2
3
4
5
6
7
8
9
10
11
public class OuterUserHomeInfo implements IOuterUserHomeInfo {
/*
* 员工的家庭信息
*/
public Map getUserHomeInfo() {
HashMap homeInfo = new HashMap();
homeInfo.put("homeTelNumbner", "员工的家庭电话是...");
homeInfo.put("homeAddress", "员工的家庭地址是...");
return homeInfo;
}
}

用户工作信息如代码清单19-18所示。

代码清单19-18 用户工作信息

1
2
3
4
5
6
7
8
9
10
11
public class OuterUserOfficeInfo implements IOuterUserOfficeInfo {
/*
* 员工的工作信息,比如,职位等
*/
public Map getUserOfficeInfo() {
HashMap officeInfo = new HashMap();
officeInfo.put("jobPosition","这个人的职位是BOSS...");
officeInfo.put("officeTelNumber", "员工的办公电话是...");
return officeInfo;
}
}

这里又到我们的核心了——适配器。好,我们来看适配器代码,如代码清单19-19所示。

代码清单19-19 适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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;
}
}

大家只要注意一下黑色字体的构造函数就可以了,它接收三个对象,其他部分变化不大,只是变量名称进行了修改,我们再来看场景类,如代码清单19-20所示。

代码清单19-20 场景类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Client {
public static void main(String[] args) {
//外系统的人员信息
IOuterUserBaseInfo baseInfo = new OuterUserBaseInfo();
IOuterUserHomeInfo homeInfo = new OuterUserHomeInfo();
IOuterUserOfficeInfo officeInfo = new OuterUserOfficeInfo();
//传递三个对象
IUserInfo youngGirl = new OuterUserInfo(baseInfo,homeInfo,officeInfo);
//从数据库中查到101个
for(int i=0;i<101;i++){
youngGirl.getMobileNumber();
}
}
}

运行的结果还是相同的。大家想想看,OuterUserInfo变成了委托服务,把IUserInfo接口需要的所有的操作都委托给其他三个接口下的实现类,它的委托是通过对象层次的关联关系进行委托的,而不是继承关系。好了,讲了这么多,我们需要给这种适配器起个名字,就是对象适配器,我们之前讲的通过继承进行的适配,叫做类适配器。对象适配器的通用类图, 如图19-9所示。

image-20210929164327675

图19-9 对象适配器类图
适配器的通用代码也比较简单,把原有的继承关系变更为关联关系就可以了,不再赘述。对象适配器和类适配器的区别是:类适配器是类间继承,对象适配器是对象的合成关系,也可以说是类的关联关系,这是两者的根本区别。二者在实际项目中都会经常用到,由于对象适配器是通过类间的关联关系进行耦合的,因此在设计时就可以做到比较灵活,比如修补源角色的隐形缺陷,关联其他对象等,而类适配器就只能通过覆写源角色的方法进行扩展,在实际项目中,对象适配器使用到场景相对较多。