概论
什么是适配器模式呢?将一个类的接口变成客户端所期待的另一种接口,从而使原本不匹配而不能再一起运作的两个类在一起工作。上文讲述的装饰模式,是包装模式,而适配器模式也是包装模式。
适配器模式示例
在互联网生态中,电商行业的发展非常的迅猛,单一应用部署和简单集群部署已经满足不了日益增长的用户数量。会发生什么事呢?对网站的请求达到一个阈值之后,只有一个结果,内存撑爆。那需要怎么做呢?系统拆分?系统拆分首先要搞清楚业务系统之间的边界。比如淘宝系统,可以划分为用户,订单,店铺,商品,仓储等等。系统划分之后需要考虑用什么技术栈。市场上出现了一种技术栈,叫RPC框架。现在市场上阿里开源RPC框架--Dubbo最为主流。另外还有spring全家桶的spring cloud 也能够解决远程调用的问题。而阿里内部是采用了内部的HSF框架,更适合阿里的内部业务。
说到系统的拆分,比如订单系统,在一个用户在购买商品时,生成订单,支付订单等等,必然需要获取到当前登录用户的信息。
首先我们在订单系统中期望有这么一个接口:
1 public interface ITargetUser { 2 3 public String getUserName(); 4 public String getAccount(); 5 public String getTelphone(); 6 public String getCompanyName(); 7 public String getCompanyAddress(); 8 public String getHomeAddress(); 9 public String getHomePhone(); 10 11 }
第3行,获取用户名称
第4行,获取用户账号
...包括手机号,公司名称,公司地址,家庭地址,家庭电话的获取。
同时,我们期望有这么一个是实现类,用来实现上面这个接口。
1 public class TargetUserImpl implements ITargetUser { 2 3 @Override 4 public String getUserName() { 5 return null; 6 } 7 8 @Override 9 public String getAccount() { 10 return null; 11 } 12 13 @Override 14 public String getTelphone() { 15 return null; 16 } 17 18 @Override 19 public String getCompanyName() { 20 return null; 21 } 22 23 @Override 24 public String getCompanyAddress() { 25 return null; 26 } 27 28 @Override 29 public String getHomeAddress() { 30 return null; 31 } 32 33 @Override 34 public String getHomePhone() { 35 return null; 36 } 37 }
上面都是我们的期望。我们把期望接口,叫作目标接口,也叫作目标角色。
而在用户系统提供出来的接口是所有信息统一封装在Map中,源代码如下图所示:
1 public interface IRemoteUser { 2 3 public Map<String, String> getUser(); 4 5 }
第3行,提供一个getUser方法。
1 public class RemoteUserImpl implements IRemoteUser { 2 3 4 @Override 5 public Map<String, String> getUser() { 6 7 Map<String, String> map = new HashMap<>(); 8 9 map.put("name", "张三"); 10 map.put("account", "zhangsan001"); 11 map.put("telphone", "13122223333"); 12 13 map.put("companyName", "阿里巴巴集团"); 14 map.put("companyAddress", "杭州市"); 15 16 map.put("homeAddress", "杭州市滨江区"); 17 map.put("homePhone", "0571-213123213"); 18 19 return map; 20 } 21 }
上面两个方法是提供好的接口已经方法,我们称之为源角色。源角色是已经提供的,但是不满足我们想要的。执行一下以下的场景类如下图所示:
1 public class Client { 2 3 public static void main(String[] args) { 4 ITargetUser user = new UserAdpter(); 5 String account = user.getAccount(); 6 7 System.out.println(account); 8 } 9 }
我们需要引入适配器,用来把源角色转化成目标角色。这也是适配器模式的核心所在。如何转化,我们遵循以下的规则:
适配器角色 extends 原角色 implements 目标接口。代码实例如下所示:
1 public class UserAdpter extends RemoteUserImpl implements ITargetUser { 2 3 private Map<String, String> map = super.getUser(); 4 5 @Override 6 public String getUserName() { 7 return map.get("name"); 8 } 9 10 @Override 11 public String getAccount() { 12 return map.get("account"); 13 } 14 15 @Override 16 public String getTelphone() { 17 return map.get("telphone"); 18 } 19 20 @Override 21 public String getCompanyName() { 22 return map.get("companyName"); 23 } 24 25 @Override 26 public String getCompanyAddress() { 27 return map.get("companyAdress"); 28 } 29 30 @Override 31 public String getHomeAddress() { 32 return map.get("homeAddress"); 33 } 34 35 @Override 36 public String getHomePhone() { 37 return map.get("homePhone"); 38 } 39 40 }
采用源角色的继承的方式,我们称为类的适配器。
除了上述演示的类适配器,还有一类适配器叫作对象适配器:对象适配器,需要持有源角色的引用。同时目标角色的接口是对象适配器类的父接口。实例代码如下图所示:
1 public class UserInstanceAdapter implements ITargetUser { 2 3 private IRemoteUser remoteUser; 4 5 public UserInstanceAdapter(IRemoteUser remoteUser) { 6 this.remoteUser = remoteUser; 7 } 8 9 @Override 10 public String getUserName() { 11 return remoteUser.getUser().get("name"); 12 } 13 14 @Override 15 public String getAccount() { 16 return remoteUser.getUser().get("account"); 17 } 18 19 @Override 20 public String getTelphone() { 21 return remoteUser.getUser().get("telphone"); 22 } 23 24 @Override 25 public String getCompanyName() { 26 return remoteUser.getUser().get("companyName"); 27 } 28 29 @Override 30 public String getCompanyAddress() { 31 return remoteUser.getUser().get("companyAddress"); 32 } 33 34 @Override 35 public String getHomeAddress() { 36 return remoteUser.getUser().get("homeAddress"); 37 } 38 39 @Override 40 public String getHomePhone() { 41 return remoteUser.getUser().get("homePhone"); 42 } 43 }
第1行:指定父接口。
第3行:持有源角色的引用。
我们再来看一下场景类Client,源码如下图所示:
1 public class Client { 2 3 public static void main(String[] args) { 4 ITargetUser user = new UserInstanceAdapter(new RemoteUserImpl()); 5 String account = user.getAccount(); 6 System.out.println(account); 7 } 8 }
执行结果就不贴了,很简单。
适配器模式的优点
1.能够使两个没有关联的类,关联起来
2.增强类的透明性,我们既可以访问目标接口,实际的动作却是委托给了源角色。
3.提高了类的复用。源角色和目标角色都可以使用。
4.非常好的灵活器。想用就用,不想用就不用,灵活性非常好。源角色有任何改动,之后调整适配角色就好,基本不改动适配器的调用方。