首页 » 设计模式之禅(第2版) » 设计模式之禅(第2版)全文在线阅读

《设计模式之禅(第2版)》19.4 适配器模式的扩展

关灯直达底部

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

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

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

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

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

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

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

public interface IOuterUserHomeInfo {     //用户的家庭信息     public Map getUserHomeInfo;}  

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

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

public interface IOuterUserOfficeInfo {     //工作区域信息     public Map getUserOfficeInfo;}  

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

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

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

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

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

public class OuterUserHomeInfo implements IOuterUserHomeInfo {     /*       * 员工的家庭信息      */     public Map getUserHomeInfo {     HashMap homeInfo = new HashMap;     homeInfo.put(/"homeTelNumbner/", /"员工的家庭电话是.../");     homeInfo.put(/"homeAddress/", /"员工的家庭地址是.../");     return homeInfo;     }}  

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

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

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 适配器

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 场景类

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所示。

图19-9 对象适配器类图

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