Java设计模式之适配器模式

概论

什么是适配器模式呢?将一个类的接口变成客户端所期待的另一种接口,从而使原本不匹配而不能再一起运作的两个类在一起工作。上文讲述的装饰模式,是包装模式,而适配器模式也是包装模式。

适配器模式示例

在互联网生态中,电商行业的发展非常的迅猛,单一应用部署和简单集群部署已经满足不了日益增长的用户数量。会发生什么事呢?对网站的请求达到一个阈值之后,只有一个结果,内存撑爆。那需要怎么做呢?系统拆分?系统拆分首先要搞清楚业务系统之间的边界。比如淘宝系统,可以划分为用户,订单,店铺,商品,仓储等等。系统划分之后需要考虑用什么技术栈。市场上出现了一种技术栈,叫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.非常好的灵活器。想用就用,不想用就不用,灵活性非常好。源角色有任何改动,之后调整适配角色就好,基本不改动适配器的调用方。

原文地址:https://www.cnblogs.com/sunshine798798/p/10087480.html