zoukankan      html  css  js  c++  java
  • 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.非常好的灵活器。想用就用,不想用就不用,灵活性非常好。源角色有任何改动,之后调整适配角色就好,基本不改动适配器的调用方。

  • 相关阅读:
    Commander Nodejs 命令行接口
    数据库集群 ---续集
    数据库集群
    实时查看linux下的日志
    自动化测试
    python中list和dict
    super与this的用法
    数据类型
    父类调用子类方法
    子类调用父类方法
  • 原文地址:https://www.cnblogs.com/sunshine798798/p/10087480.html
Copyright © 2011-2022 走看看