(9)spring常用模式--------适配器模式

1.适配器模式简介

适配器模式:将一个类的转接口转换成客户希望的另外一个接口,适配器模式使得原来由于接口不兼容的而不能的工作的哪些类可以工作,主要作用就是兼容

应用场景: 编码解码、 一拖三充电头、HDMI 转 VGA

在spring的体现: Spring AOP 模块对 BeforeAdvice、 AfterAdvice、 ThrowsAdvice 三种通知类型的支持实际上是借助适配器模式来实现的, 这样的好处是使得框架允许用户向框架中加入自己想要支持的任何一种通知类型, 上述三种通知类型是 Spring AOP 模块定义的, 它们是 AOP 联盟定义的 Advice 的子类型。 在spring中 基本adapter结尾都是适配器

先看一下适配器模式的一种方式对象适配器的uml图


适配器模式.png

可以看到Adapter适配器包含了Adeptee适配者的引用,该适配器的作用把adaptee适配者转换成target接口,实现了接口的转换问题。

2.适配器模式分类

有三种分类:

  • 类适配器 (通过引用适配者进行组合实现)
  • 对象适配器(通过继承适配者进行实现)
  • 接口适配器 (通过抽象类来实现适配)

前二者在实现上有些许区别,作用一样,第三个接口适配器差别较大。

3.适配器的实例讲解

  • (1)类适配器模式
    原理:通过继承来实现适配器功能。

当我们要访问的接口A中没有我们想要的方法 ,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器p来进行中转,这个适配器p要实现我们访问的接口A,这样我们就能继续访问当前接口A中的方法(虽然它目前不是我们的菜),然后再继承接口B的实现类,这样我们可以在适配器P中访问接口B的方法了,这时我们在适配器P中的接口A方法中直接引用B中的合适方法,这样就完成了一个简单的类适配器。

下面的例子为:通过把普通登录的接口转换成微信登录的接口

登录返回状态的类

/**
 * @Project: spring
 * @description: 登录返回的结果的类
 * @author: sunkang
 * @create: 2018-09-05 20:55
 * @ModificationHistory who      when       What
 **/
public class ResultMsg {
    private  String code;
    private  String msg;
    private  Object data;

    public ResultMsg(String code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
}

普通登录的接口

/**
 * @Project: spring
 * @description:   登录的接口业务
 * @author: sunkang
 * @create: 2018-09-05 20:51
 * @ModificationHistory who      when       What
 **/
public interface ISiginSerevice {
    ResultMsg login(String username,String password);
}

登录的具体实现

* @Description:
* @Param: 登录的具体实现
* @return:
* @Author: sunkang
* @Date: 2018/9/5
*/
public class SiginService implements ISiginSerevice {
    /**
     * 登录的方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username,String password){
        return  new ResultMsg("200","登录成功",new Object());
    }
}

通过微信接口登录

/**
 * @Project: spring
 * @description:  通过微信接口登录
 * @author: sunkang
 * @create: 2018-09-05 20:58
 * @ModificationHistory who      when       What
 **/
public interface ISiginForWebChat {
    /**
     * 通过微信登录
     * @param openId
     * @return
     */
     ResultMsg loginForWechat(String openId);
}

类适配器(把原有的登录方法适配成微信登录的接口)

/**
 * @Project: spring
 * @description:  类适配器   目标接口为ISiginForWebChat,但是适配者的接口为ISiginService
 * @author: sunkang
 * @create: 2018-09-05 21:29
 * @ModificationHistory who      when       What
 **/
public class ClassSiginForWebChatAdapter  extends  SiginService implements  ISiginForWebChat{
    @Override
    public ResultMsg loginForWechat(String openId) {
        return login(openId,null);
    }
}
  • (2)对象适配器模式

原理:通过组合来实现适配器功能。

由于比较简单,应该是比较清楚的,代码如下

/**
 * @Project: spring
 * @description:  对象适配器  默认通过组合来实现的
 * @author: sunkang
 * @create: 2018-09-05 21:06
 * @ModificationHistory who      when       What
 **/
public class ObjectSiginForWebChatAdapter implements ISiginForWebChat {
    private ISiginSerevice siginSerevice;

    public ObjectSiginForWebChatAdapter(ISiginSerevice siginSerevice) {
        this.siginSerevice = siginSerevice;
    }
    @Override
    public ResultMsg loginForWechat(String openId) {
        return siginSerevice.login(openId,null);
    }
}
  • (3)接口适配器模式
    原理:通过抽象类来实现适配,这种适配稍别于上面所述的适配。

当存在这样一个接口,其中定义了N多的方法,而我们现在却只想使用其中的一个到几个方法,如果我们直接实现接口,那么我们要对所有的方法进行实现,哪怕我们仅仅是对不需要的方法进行置空(只写一对大括号,不做具体方法实现)也会导致这个类变得臃肿,调用也不方便,这时我们可以使用一个抽象类作为中间件,即适配器,用这个抽象类实现接口,而在抽象类中所有的方法都进行置空,那么我们在创建抽象类的继承类,而且重写我们需要使用的那几个方法即可。

比如现在登录要支持第三方接口,比如通过qq登录,通过微信登录,通过手机号和验证码登录,但是有的实现类不是通通用到这些方法,可以用一个抽象类来实现接口的所有的方法,作为一个默认的实现,然后具体的实现类继承抽>象类,要用到哪个方法就重写哪个方法

第三方登录的接口

/**
 * @Project: spring
 * @description:   第三方登录的接口
 * @author: sunkang
 * @create: 2018-09-05 21:41
 * @ModificationHistory who      when       What
 **/
public interface ISiginForThirdService {
    /**
     * 通过qq登录
     * @param openId
     * @return
     */
     ResultMsg loginForQQ(String openId);
    /**
     * 通过微信登录
     * @param openId
     * @return
     */
     ResultMsg loginForWechat(String openId);

    /**
     * 通过手机号和验证码登录
     * @param telphone
     * @param code
     * @return
     */
     ResultMsg loginForTelphone(String telphone,String code);
}

抽象适配器

/**
 * @Project: spring
 * @description:   抽象适配器   因为接口太多,所以用了一个抽象的类的适配器,来默认实现
 * @author: sunkang
 * @create: 2018-09-05 21:43
 * @ModificationHistory who      when       What
 **/
public abstract class SiginForThirdAdapter implements  ISiginForThirdService {
    @Override
    public ResultMsg loginForQQ(String openId) {
        return new ResultMsg("200","qq登录成功",new Object());
    }

    @Override
    public ResultMsg loginForWechat(String openId) {
        return new ResultMsg("200","微信登录成功",new Object());
    }
    @Override
    public ResultMsg loginForTelphone(String telphone, String code) {
        return new ResultMsg("200","手机登录成功",new Object());
    }
}

通过电话号码和动态密码登录,只需要重写对应的方法接口

/**
 * @Project: spring
 * @description:  只需要用电话号码登录
 * @author: sunkang
 * @create: 2018-09-05 21:44
 * @ModificationHistory who      when       What
 **/
public class LoginForTelphoneAdapter extends SiginForThirdAdapter {
    public ResultMsg loginForTelphone(String telphone, String code) {
        System.out.println("通过电话号码登录");
        ResultMsg msg = new ResultMsg("200","qq登录成功",new Object());
        System.out.println("登录的结果为"+msg.getMsg());
        return msg;
    }
}

三种方式的测试案例

/**
 * @Project: spring
 * @description:  三种方式实现的测试案例
 * @author: sunkang
 * @create: 2018-09-05 21:17
 * @ModificationHistory who      when       What
 **/
public class AdapterTest {
    public static void main(String[] args) {
        //需要适配的类,要把  ISiginSerevice 变成 ISiginForWebChat
        ISiginSerevice siginSerevice  = new SiginService();
        ISiginForWebChat siginForThreadService  = new ObjectSiginForWebChatAdapter(siginSerevice);

        //对象适配器 ,传的是一个适配的类,通过组合的方式
        ResultMsg resultMsg =   siginForThreadService.loginForWechat("1231321ffasfasffad");
        System.out.println(resultMsg.getMsg());

        //类适配器,通过继承实现的
        ResultMsg msg =  new ClassSiginForWebChatAdapter().loginForWechat("1231321ffasfasffad");
        System.out.println(msg.getMsg());

        //接口适配器 ,主要实现了一个抽象的适配器
        //但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口
        //并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器
        ISiginForThirdService siginForWebChat = new LoginForTelphoneAdapter();
        ResultMsg telPoneMsg =  siginForWebChat.loginForTelphone("13588304966","1234");
        System.out.println(telPoneMsg.getMsg());
    }
}

4、适配器模式应用场景

类适配器与对象适配器的使用场景一致,仅仅是实现手段稍有区别,二者主要用于如下场景:

  • 想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法
  • 我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。

以上两个场景其实就是从两个角度来描述一类问题,那就是要访问的方法不在合适的接口里,一个从接口出发(被访问),一个从访问出发(主动访问)。

接口适配器使用场景

  • 想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。