设计模式08-适配器模式与桥接模式详解

1.8设计模式-适配器模式与桥接模式详解

1.8.1.适配器模式详解

时长:1h6min

学习目标
》掌握适配器和桥接模式的应用场景

》重构第三方登录自由适配的业务场景

》了解模式在源码中应用

》优缺点

8.1.1.适配器模式的定义

定义

  适配器模式,Adapter Pattern,又称变压器模式。它是功能是一个接口变成客户所期望的另一种接口,从而使

原本因接口不匹配而导致无法在一起工作的两个类能够一起工作。

  属于结构型设计模式。

生活中适配器模式的应用

》两脚插转三角插座

》手机充电接口

》显示器转接头

8.1.1.1.应用场景

 1.已经存在的类,它的方法与需求不匹配(方法结果相同或相似的情况)

 2.适配器模式不是软件设计阶段考虑的设计模式,而是随着软件维护,由于不同产品,不同厂家造成

功能类似而接口不相同的情况下的解决方案。

8.1.2.适配器模式的实现方案

 8.1.2.1.通用写法

  适配器有3个角色,目标角色【期望兼容的角色】,原角色【系统中已存在角色】,适配器

适配器有3种形式:

  》类适配器

  》对象适配器

  》接口适配器

1.类适配器写法

【1】标准实现类图

 2.对象适配器写法
【1】标准类图

【2】代码实现

A.适配原角色

package com.wf.adapter.general.objectadapter;

/**
 * @ClassName Adaptee
 * @Description 适配原角色
 * @Author wf
 * @Date 2020/6/9 14:47
 * @Version 1.0
 */
public class Adaptee {
    public int specificRequest(){
        return 220;
    }
}

B.目标对象【实现是接口】

package com.wf.adapter.general.objectadapter;

/**
 * @ClassName Target
 * @Description 目标对象
 * @Author wf
 * @Date 2020/6/9 14:48
 * @Version 1.0
 */
public interface Target {
    int request();
}

C.适配器类

package com.wf.adapter.general.objectadapter;

/**
 * @ClassName Adapter
 * @Description 对象适配器,组合引用原角色,也得实现目标对象的功能
 * @Author wf
 * @Date 2020/6/9 14:48
 * @Version 1.0
 */
public class Adapter implements Target {
    //组合引用
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public int request() {
        return adaptee.specificRequest() / 10;
    }
}

D.测试类

package com.wf.adapter.general.objectadapter;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/6/9 14:49
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        Target adapter = new Adapter(new Adaptee());
        int result = adapter.request();
        System.out.println("最终结果:"+result);
    }
}

说明:

  客户调用时,使用多态的方式。如此,就只会出现接口的功能,而不会出现原角色的功能调用。

 3.接口适配器写法

它的使用场景:

  它是用于解决,需要适配功能很多的情况,并且都与适配原角色相关,如果针对每一个功能都写一个适配器,就会出现很多的类,

造成类臃肿。  

  所以,就想到把这多个功能,归类到一个目标对象中,统一创建一个接口适配器,就可以达到一个类适配多个功能的目的。

   解决方案:通过接口适配器,只适配需要的接口方法。

代码如下所示:

【1】适配原角色

package com.wf.adapter.general.interfaceadapter;

/**
 * @ClassName Adaptee
 * @Description 适配原角色
 * @Author wf
 * @Date 2020/6/9 14:47
 * @Version 1.0
 */
public class Adaptee {
    public int specificRequest(){
        return 220;
    }
}

【2】目标对象

package com.wf.adapter.general.interfaceadapter;

/**
 * @ClassName Target
 * @Description 目标对象
 * @Author wf
 * @Date 2020/6/9 14:48
 * @Version 1.0
 */
public interface Target {
    int request0();
    int request1();
    int request2();
    int request3();
    int request4();
}

说明:

  可以看到,新增目标对象需要很多方法,并且都与适配原角色有关联。

  因此,把这些方法,统一放到一个目标接口中,而不是定义多个目标类。

【3】适配器类

package com.wf.adapter.general.interfaceadapter;

/**
 * @ClassName Adapter
 * @Description 接口适配器,组合引用原角色,也得实现目标对象的功能
 * @Author wf
 * @Date 2020/6/9 14:48
 * @Version 1.0
 */
public class Adapter implements Target {
    //组合引用
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public int request0() {
        return 0;
    }

    @Override
    public int request1() {
        return 0;
    }

    @Override
    public int request2() {
        return 0;
    }

    @Override
    public int request3() {
        return 0;
    }

    @Override
    public int request4() {
        return 0;
    }
}

【4】测试类

package com.wf.adapter.general.interfaceadapter;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/6/9 14:49
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        Target adapter = new Adapter(new Adaptee());
        int result = adapter.request0();
        System.out.println("最终结果:"+result);

        result = adapter.request2();
        System.out.println("最终结果:"+result);
    }
}
8.1.2.2.具体实例
1.类适配器的使用

  这里使用变压器模式来,进行示例说明。

  手机充电器,就是典型的适配器模式。适配器,最初是电工学上一个术语。后来被发展为软件术语。

  手机充电器,获得手机需要的直流电【5v电压】,这可以当作目标对象

  而家电电压是220v交流电,可以视为适配器原角色。

  充电器,把220v交流电压,转换为5v直流电压。就是适配器

系统实现代码如下:

【1】适配原角色,220v交流电压

package com.wf.adapter.demo.classadapter;

/**
 * @ClassName AC220
 * @Description 交流电压220V
 * @Author wf
 * @Date 2020/6/3 14:36
 * @Version 1.0
 */
public class AC220 {
    public int outputAC220V(){
        int output = 220;
        System.out.println("输出电压:"+output+" V");
        return output;
    }
}

【2】定义目标对象【实际是接口】

package com.wf.adapter.demo.classadapter;

/**
 * @ClassName DC5
 * @Description 直流电压5v
 * @Author wf
 * @Date 2020/6/3 14:39
 * @Version 1.0
 */
public interface DC5 {
    int output5V();
}

【3】充电器,适配器

package com.wf.adapter.demo.classadapter;

/**
 * @ClassName PowerAdapter
 * @Description 电源适配器
 * @Author wf
 * @Date 2020/6/3 14:40
 * @Version 1.0
 */
public class PowerAdapter extends AC220 implements DC5 {
    @Override
    public int output5V() {
        int adapterInput = super.outputAC220V();
        int adapterOutput = adapterInput / 44;
        System.out.println("使用Adapter输入AC"+adapterInput + "V,输出DC["+adapterOutput +"]V");
        return adapterOutput;
    }
}

 说明:

  适配器类中,继承适配原角色,实现目标接口

  实现接口的功能,调用原角色的功能,并进行扩展。

【4】测试类

package com.wf.adapter.demo.classadapter;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/6/3 14:44
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        PowerAdapter adapter = new PowerAdapter();
        adapter.output5V();
    }
}

测试结果如下:

 说明:

  由于适配器,继承原角色,并实现目标接口。所以,适配器具有两者的功能。

  显然,有些违背最少知道原则。

  

  因此,提出对象适配器写法。它的实现思路:

弃用继承结构,而采用组合方式

2.对象适配器写法,改写系统
【1】适配原角色

不需要修改,如下所示:

package com.wf.adapter.demo.objectadapter;

/**
 * @ClassName AC220
 * @Description 交流电压220V
 * @Author wf
 * @Date 2020/6/3 14:36
 * @Version 1.0
 */
public class AC220 {
    public int outputAC220V(){
        int output = 220;
        System.out.println("输出电压:"+output+" V");
        return output;
    }
}
【2】目标对象

代码逻辑不变,如下所示:

package com.wf.adapter.demo.objectadapter;

/**
 * @ClassName DC5
 * @Description 直流电压5v
 * @Author wf
 * @Date 2020/6/3 14:39
 * @Version 1.0
 */
public interface DC5 {
    int output5V();
}

 【3】适配器类

package com.wf.adapter.demo.objectadapter;

/**
 * @ClassName PowerAdapter
 * @Description 电源适配器
 * @Author wf
 * @Date 2020/6/3 14:40
 * @Version 1.0
 */
public class PowerAdapter implements DC5 {
    private AC220 ac220;

    public PowerAdapter(AC220 ac220) {
        this.ac220 = ac220;
    }

    @Override
    public int output5V() {
        int adapterInput = ac220.outputAC220V();
        int adapterOutput = adapterInput / 44;
        System.out.println("使用Adapter输入AC"+adapterInput + "V,输出DC["+adapterOutput +"]V");
        return adapterOutput;
    }
}

说明:

  采用组合引用,来代替继承方式。

【4】测试类
package com.wf.adapter.demo.objectadapter;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/6/3 14:44
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        DC5 adapter = new PowerAdapter(new AC220());
        adapter.output5V();
    }
}
3.接口适配器案例

  针对上面的系统,适配器需要转换多种电压,如:5v,12v,24v...

  只需要把这多个功能,统一归集到一个目标类,然后一个适配器就可以实现。

【1】适配原角色
package com.wf.adapter.demo.interfaceadapter;

/**
 * @ClassName AC220
 * @Description 交流电压220V
 * @Author wf
 * @Date 2020/6/3 14:36
 * @Version 1.0
 */
public class AC220 {
    public int outputAC220V(){
        int output = 220;
        System.out.println("输出电压:"+output+" V");
        return output;
    }
}
【2】目标对象

内部提供多个功能:

package com.wf.adapter.demo.interfaceadapter;

/**
 * @ClassName DC
 * @Description 目标类,实现多个转换功能
 * @Author wf
 * @Date 2020/6/9 16:04
 * @Version 1.0
 */
public interface DC {
    int output5V();
    int output12V();
    int output24V();
    int output36V();
}
【3】适配器
package com.wf.adapter.demo.interfaceadapter;

import com.wf.adapter.demo.objectadapter.AC220;
import com.wf.adapter.demo.objectadapter.DC5;

/**
 * @ClassName PowerAdapter
 * @Description 电源适配器
 * @Author wf
 * @Date 2020/6/3 14:40
 * @Version 1.0
 */
public class PowerAdapter implements DC {
    private AC220 ac220;

    public PowerAdapter(AC220 ac220) {
        this.ac220 = ac220;
    }

    @Override
    public int output5V() {
        int adapterInput = ac220.outputAC220V();
        int adapterOutput = adapterInput / 44;
        System.out.println("使用Adapter输入AC"+adapterInput + "V,输出DC["+adapterOutput +"]V");
        return adapterOutput;
    }

    @Override
    public int output12V() {
        return 0;
    }

    @Override
    public int output24V() {
        return 0;
    }

    @Override
    public int output36V() {
        return 0;
    }
}
【4】测试类
package com.wf.adapter.demo.interfaceadapter;

import com.wf.adapter.demo.objectadapter.AC220;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/6/9 16:12
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        DC adapter = new PowerAdapter(new AC220());
        adapter.output5V();
    }
}
 8.1.2.3.开发中适配器模式应用案例
1.系统需求

  是一个管理平台登录场景。在大量的老系统中,需要用户注册,然后登录,才能使用平台。

但是,当大量用户使用时,这种帐号登录的方式,管理起来反而很复杂。

  所以,从而使用员工的qq,或微信号进行登录,从而免去注册的过程。

  然而,系统的注册,登录功能仍然不能修改,这时就可以使用适配器模式,来解决系统兼容性问题。

2.代码实现

【1】原有登录系统功能

A.业务类,员工类

package com.wf.adapter.demo.passport;

/**
 * @ClassName Member
 * @Description 成员类
 * @Author wf
 * @Date 2020/6/9 16:18
 * @Version 1.0
 */
public class Member {
    private String username;
    private String password;
    private String mid;
    private String info;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getMid() {
        return mid;
    }

    public void setMid(String mid) {
        this.mid = mid;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

B.结果集pojo

package com.wf.adapter.demo.passport;

/**
 * @ClassName ResultMsg
 * @Description 结果集
 * @Author wf
 * @Date 2020/6/9 16:19
 * @Version 1.0
 */
public class ResultMsg {
    private int code;
    private String msg;
    private Object data;
    public ResultMsg(int code, String msg,String data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int 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;
    }
}

C.登录注册服务

package com.wf.adapter.demo.passport;

/**
 * @ClassName PassportService
 * @Description 登录,注册服务
 * @Author wf
 * @Date 2020/6/9 16:18
 * @Version 1.0
 */
public class PassportService {
    public ResultMsg register(String username,String password){
        return new ResultMsg(200,"注册成功",null);
    }
    public ResultMsg login(String username,String password){
        return null;
    }
}

【2】适配器扩展系统 

A.目标类【定义为接口】

这里需要实现第三方登录,如:qq登录。就需要使用腾训提供open api【需要传参openId】,如下所示:

package com.wf.adapter.demo.passport;

/**
 * @ClassName IPassortForThird
 * @Description 第三方登录,目标类
 * @Author wf
 * @Date 2020/6/10 10:06
 * @Version 1.0
 */
public interface IPassportForThird {
    /**
     * qq登录
     * @param openId
     * @return
     */
    ResultMsg loginForQQ(String openId);

    /**
     * 微信号登录
     * @param openId
     * @return
     */
    ResultMsg loginForWeChat(String openId);

    /**
     * 内部员工token登录
     * @param token
     * @return
     */
    ResultMsg loginForToken(String token);

    /**
     * 手机号+验证码登录
     * @param phone
     * @param code
     * @return
     */
    ResultMsg loginForTelephone(String phone, String code);
}

B.适配器类

  

  这里使用,类适配器实现。

package com.wf.adapter.demo.passport;

/**
 * @ClassName PassportForThirdAdapter
 * @Description 第三方登录适配器,类适配器
 * @Author wf
 * @Date 2020/6/10 10:15
 * @Version 1.0
 */
public class PassportForThirdAdapter extends PassportService implements IPassportForThird {
    @Override
    public ResultMsg loginForQQ(String openId) {
        return loginForRegister(openId,null);
    }

    @Override
    public ResultMsg loginForWeChat(String openId) {
        return loginForRegister(openId,null);
    }

    @Override
    public ResultMsg loginForToken(String token) {
        return loginForRegister(token,null);
    }

    @Override
    public ResultMsg loginForTelephone(String phone, String code) {
        return loginForRegister(phone,null);
    }

    /**
     * 提供一个老的登录逻辑,先注册,再登录
     * 当使用第三方登录时,不能够得到密码,处理办法是:如果密码为null,约定一个特殊密码。
     * 后台做一个标识,只要得到这个特殊密码,都表示第三方登录,走特殊流程
     * @param username
     * @param password
     * @return
     */
    private ResultMsg loginForRegister(String username, String password){
        if(null == password){
            password = "THIRD_EMPTY";
        }
        super.register(username,password);
        return super.login(username,password);
    }
}

3.测试类

package com.wf.adapter.demo.passport;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/6/10 10:25
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        IPassportForThird service = new PassportForThirdAdapter();
        //给一个openId,不会是qq号,为了保护用户隐私
        service.loginForQQ("owpgnw");
    }
}

 

说明:

  功能基本完成。但是,由于适配器内部需要进行各种openAPI接入,导致类的职责过重

而且,当系统进行需求扩展时,如:需要支持抖音帐号登录。就需要修改适配器原有代码,不符合开闭原则。

4.扩展需求,增加抖音帐号登录

由于功能扩展,原来的适配器类,可能职责过重,依赖大量openAPI.

因此,可以对系统进行拆分,针对每一种登录,实现一个适配器,来处理登录逻辑。

【1】目标类【实为接口】

package com.wf.adapter.demo.passport.v2;

import com.wf.adapter.demo.passport.ResultMsg;

/**
 * @ClassName IPassportForThird
 * @Description 第三方登录,目标类
 * @Author wf
 * @Date 2020/6/10 10:06
 * @Version 1.0
 */
public interface IPassportForThird {
    /**
     * qq登录
     * @param openId
     * @return
     */
    ResultMsg loginForQQ(String openId);

    /**
     * 微信号登录
     * @param openId
     * @return
     */
    ResultMsg loginForWeChat(String openId);

    /**
     * 内部员工token登录
     * @param token
     * @return
     */
    ResultMsg loginForToken(String token);

    /**
     * 手机号+验证码登录
     * @param phone
     * @param code
     * @return
     */
    ResultMsg loginForTelephone(String phone, String code);
}

【2】适配器中转类

package com.wf.adapter.demo.passport.v2;

import com.wf.adapter.demo.passport.PassportService;
import com.wf.adapter.demo.passport.ResultMsg;

/**
 * @ClassName PassportForThirdAdapter
 * @Description 第三方登录适配,中间实现类
 * @Author wf
 * @Date 2020/6/10 10:15
 * @Version 1.0
 */
public class PassportForThirdAdapter implements IPassportForThird {
    @Override
    public ResultMsg loginForQQ(String openId) {
        return processLogin(openId,LoginForQQAdapter.class);
    }

    @Override
    public ResultMsg loginForWeChat(String openId) {
        return processLogin(openId,LoginForWeChatAdapter.class);
    }

    @Override
    public ResultMsg loginForToken(String token) {
        return processLogin(token,LoginForTokenAdapter.class);
    }

    @Override
    public ResultMsg loginForTelephone(String phone, String code) {
        return processLogin(phone,LoginForTelephoneAdapter.class);
    }

    private ResultMsg processLogin(String id, Class<? extends ILoginAdapter> clazz){
        try {
            ILoginAdapter adapter = clazz.newInstance();
            if(adapter.support(adapter)){
                return adapter.login(id,adapter);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

}

【3】顶层适配器接口

package com.wf.adapter.demo.passport.v2;

import com.wf.adapter.demo.passport.ResultMsg;

/**
 * @ClassName ILoginAdapter
 * @Description 适配器公共接口
 * @Author wf
 * @Date 2020/6/10 10:36
 * @Version 1.0
 */
public interface ILoginAdapter {
    /**
     * 匹配适配器子类实现
     * @param object
     * @return
     */
    boolean support(Object object);

    /**
     * 登录逻辑
     * @param id
     * @param adapter
     * @return
     */
    ResultMsg login(String id,Object adapter);
}

【4】单个适配器实现

package com.wf.adapter.demo.passport.v2;


/**
 * @ClassName LoginForQQAdapter
 * @Description 适配器
 * @Author wf
 * @Date 2020/6/10 10:45
 * @Version 1.0
 */
public class LoginForQQAdapter extends AbstractAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof LoginForQQAdapter;
    }

}
package com.wf.adapter.demo.passport.v2;

import com.wf.adapter.demo.passport.ResultMsg;

/**
 * @ClassName LoginForTelephoneAdapter
 * @Description TODO
 * @Author wf
 * @Date 2020/6/10 14:02
 * @Version 1.0
 */
public class LoginForTelephoneAdapter extends AbstractAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof LoginForTelephoneAdapter;
    }
}

package com.wf.adapter.demo.passport.v2;

/**
 * @ClassName LoginForTokenAdapter
 * @Description 内部员工登录,适配器
 * @Author wf
 * @Date 2020/6/10 14:00
 * @Version 1.0
 */
public class LoginForTokenAdapter extends AbstractAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof LoginForTokenAdapter;
    }
}

package com.wf.adapter.demo.passport.v2;

/**
 * @ClassName loginForWeChatAdapter
 * @Description 第三方登录,微信登录
 * @Author wf
 * @Date 2020/6/10 13:57
 * @Version 1.0
 */
public class LoginForWeChatAdapter extends AbstractAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof LoginForWeChatAdapter;
    }
}

【5】抽取适配器公共实现

package com.wf.adapter.demo.passport.v2;

import com.wf.adapter.demo.passport.PassportService;
import com.wf.adapter.demo.passport.ResultMsg;

/**
 * @ClassName AbstractAdapter
 * @Description 提取公共代码
 * @Author wf
 * @Date 2020/6/10 10:52
 * @Version 1.0
 */
public abstract class AbstractAdapter extends PassportService implements ILoginAdapter{

    /**
     * 提供一个老的登录逻辑,先注册,再登录
     * 当使用第三方登录时,不能够得到密码,处理办法是:如果密码为null,约定一个特殊密码。
     * 后台做一个标识,只要得到这个特殊密码,都表示第三方登录,走特殊流程
     * @param username
     * @param password
     * @return
     */
    protected ResultMsg loginForRegister(String username, String password){
        if(null == password){
            password = "THIRD_EMPTY";
        }
        super.register(username,password);
        return super.login(username,password);
    }

    @Override
    public ResultMsg login(String id, Object adapter) {
        return loginForRegister(id,null);
    }
}

【6】测试类

package com.wf.adapter.demo.passport.v2;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/6/10 14:05
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        IPassportForThird adapter = new PassportForThirdAdapter();
        adapter.loginForQQ("qpgqjgpwokoekfkfod");
    }
}

说明:

  如果要扩展抖音帐号登录,需要修改目标接口及实现。并且增加一个适配器实现子类。

  这只能在一定程度上,实现动态扩展的目的。但并不能够真正地满足开闭原则。

【7】系统类图

 2.1.3.适配器模式在源码中应用

2.1.3.1.spring中aop模块
public interface AdvisorAdapter {
    boolean supportsAdvice(Advice var1);

    MethodInterceptor getInterceptor(Advisor var1);
}

实现类如下:

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    MethodBeforeAdviceAdapter() {
    }

    public boolean supportsAdvice(Advice advice) {
        return advice instanceof MethodBeforeAdvice;
    }

    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }
}
2.1.3.2.spring中mvc模块
public interface HandlerAdapter {
    boolean supports(Object var1);

    @Nullable
    ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    long getLastModified(HttpServletRequest var1, Object var2);
}

适配器各实现子类:

 实现子类:

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    public SimpleControllerHandlerAdapter() {
    }

    public boolean supports(Object handler) {
        return handler instanceof Controller;
    }

    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return ((Controller)handler).handleRequest(request, response);
    }

    public long getLastModified(HttpServletRequest request, Object handler) {
        return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
    }
}

适配器的使用:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            Iterator var2 = this.handlerAdapters.iterator();

            while(var2.hasNext()) {
                HandlerAdapter ha = (HandlerAdapter)var2.next();
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Testing handler adapter [" + ha + "]");
                }

                if (ha.supports(handler)) {  //判断使用哪一种适配器
                    return ha;
                }
            }
        }

        throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }

2.1.4.适配器模式总结

2.1.4.1.优点,缺点总结

优点:

  能提高类的透明性和复用性,现有的类复用且不需要改变。

  目标类和适配器类解耦,提高程序的扩展性

  在很多业务场景中符合开闭原则

缺点:

  》适配器编写过程,需要全面考虑,可能会增加系统的复杂性

  》增加代码的阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

1.8.2.桥接模式详解

时长:58min

8.2.1.桥接模式定义

定义

  Bridge Pattern,也称桥梁模式,接口模式,或柄体(Handle and Body)模式,是将抽象部分与它的具体

实现部分分离,使它们可以独立变化的。

  通过组合的方式建立两个类之间的联系,而不使用继承。

  属于结构型模式。

桥接模式在生活中的应用场景

1.连接两个空间的拱桥

2.虚拟网络与真实网络的桥接

8.2.1.1.适用场景

1.在抽象和具体实现之间需要增加更多的灵活性的场景

2.一个类存在两个(或多个)独立变化的维度,而这两个(或多个)维度都需要独立进行扩展

3.不希望继承,或因为多层继承导致系统类的个数剧增【多用组合引用】

8.2.2.桥接模式的写法

8.2.2.1.通用写法
1.系统类图设计

 2.代码实现

A.顶层接口

package com.wf.bridge.general;

/**
 * @ClassName IImplementor
 * @Description 顶层接口定义
 * @Author wf
 * @Date 2020/6/10 15:28
 * @Version 1.0
 */
public interface IImplementor {
    void operationImpl();
}

B.接口实现

package com.wf.bridge.general;

/**
 * @ClassName ConcreteImplementorA
 * @Description 实现
 * @Author wf
 * @Date 2020/6/10 15:29
 * @Version 1.0
 */
public class ConcreteImplementorA implements IImplementor {
    @Override
    public void operationImpl() {
        System.out.println("I'm ConcreteImplementorA");
    }
}
package com.wf.bridge.general;

/**
 * @ClassName ConcreteImplementorB
 * @Description 接口实现B
 * @Author wf
 * @Date 2020/6/10 15:29
 * @Version 1.0
 */
public class ConcreteImplementorB implements IImplementor {
    @Override
    public void operationImpl() {
        System.out.println("I'm ConcreteImplementorB");
    }
}

C.抽象类组合引用接口实例

package com.wf.bridge.general;

/**
 * @ClassName Abstraction
 * @Description 抽象类,组合引用接口实例
 * @Author wf
 * @Date 2020/6/10 15:27
 * @Version 1.0
 */
public abstract class Abstraction{
    protected IImplementor mImplementor;

    public Abstraction(IImplementor mImplementor) {
        this.mImplementor = mImplementor;
    }

    public void operation(){
        this.mImplementor.operationImpl();
    }
}

说明:

  这里很像是包装器模式。

D.修改抽象实现

package com.wf.bridge.general;

/**
 * @ClassName RefinedAbstraction
 * @Description 修正抽象
 * @Author wf
 * @Date 2020/6/10 15:31
 * @Version 1.0
 */
public class RefinedAbstraction extends Abstraction {

    public RefinedAbstraction(IImplementor mImplementor) {
        super(mImplementor);
    }

    @Override
    public void operation() {
        super.operation();
        System.out.println("refined operation");
    }
}

E.测试类

package com.wf.bridge.general;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/6/10 15:32
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        RefinedAbstraction abstraction = new RefinedAbstraction(new ConcreteImplementorA());
        abstraction.operation();
    }
}
8.2.2.2.桥接模式具体案例应用
1.需求分析

  在前面的抽象工厂模式学习中,涉及产品簇和产品等级的实现案例。

  产品簇,可以理解为产品的实现维度

  产品等级,理解为产品的抽象维度。  

  从而,可以使用桥接模式,将两种关系联系起来。

具体需求:

  针对学习课程Course,课程内容有:笔记,源码,ppt,课后作业【理解为产品抽象

  不同类型的课程:java,Python,AI,大数据。。。【理解为产品实现

2.代码实现

【1】定义产品实现顶层接口--课程

package com.wf.bridge.demo.course;

/**
 * @ClassName ICourse
 * @Description 课程,顶层接口定义
 * @Author wf
 * @Date 2020/6/10 15:58
 * @Version 1.0
 */
public interface ICourse {
}

实现类:

package com.wf.bridge.demo.course;

/**
 * @ClassName JavaCourse
 * @Description java课程
 * @Author wf
 * @Date 2020/6/10 16:00
 * @Version 1.0
 */
public class JavaCourse implements ICourse {

}
package com.wf.bridge.demo.course;

/**
 * @ClassName PythonCourse
 * @Description Python课程
 * @Author wf
 * @Date 2020/6/10 16:01
 * @Version 1.0
 */
public class PythonCourse implements ICourse {
}

【2】增加第二个维度的接口及实现----笔记

package com.wf.bridge.demo.course;

/**
 * @ClassName INote
 * @Description 笔记接口
 * @Author wf
 * @Date 2020/6/10 16:02
 * @Version 1.0
 */
public interface INote {
    void edit();
}

package com.wf.bridge.demo.course;

/**
 * @ClassName JavaNote
 * @Description java课程笔记
 * @Author wf
 * @Date 2020/6/10 16:03
 * @Version 1.0
 */
public class JavaNote implements INote {
    @Override
    public void edit() {

    }
}

package com.wf.bridge.demo.course;

/**
 * @ClassName PythonNote
 * @Description Python笔记
 * @Author wf
 * @Date 2020/6/10 16:03
 * @Version 1.0
 */
public class PythonNote implements INote {
    @Override
    public void edit() {

    }
}

【3】增加第三个维度的接口及实现----视频

package com.wf.bridge.demo.course;

/**
 * @ClassName IVideo
 * @Description 视频接口
 * @Author wf
 * @Date 2020/6/10 16:04
 * @Version 1.0
 */
public interface IVideo {
}
package com.wf.bridge.demo.course;

/**
 * @ClassName JavaVideo
 * @Description TODO
 * @Author wf
 * @Date 2020/6/10 16:05
 * @Version 1.0
 */
public class JavaVideo implements IVideo {
}
package com.wf.bridge.demo.course;

/**
 * @ClassName PythonVideo
 * @Description TODO
 * @Author wf
 * @Date 2020/6/10 16:05
 * @Version 1.0
 */
public class PythonVideo implements IVideo {
}

说明:

  现在,产品的三个维度是相互独立的,互不相关。

  想要它们关联起来,因此,使用桥接模式,创建抽象,引用接口实例。、

【4】创建抽象,引用接口实现

package com.wf.bridge.demo.course;

/**
 * @ClassName AbstractCourse
 * @Description 课程抽象
 * @Author wf
 * @Date 2020/6/10 16:14
 * @Version 1.0
 */
public class AbstractCourse implements ICourse {
    private IVideo video;
    private INote note;

    public void setVideo(IVideo video) {
        this.video = video;
    }

    public void setNote(INote note) {
        this.note = note;
    }

    @Override
    public String toString() {
        return "AbstractCourse{" +
                "video=" + video +
                ", note=" + note +
                '}';
    }
}

说明:

  因为Course存在不同实现,当提供这样一个抽象之后,原来的课程实现,就需要修改。

  不是实现接口,而是直接继承这个抽象,修改后如下所示:

package com.wf.bridge.demo.course;

/**
 * @ClassName JavaCourse
 * @Description java课程
 * @Author wf
 * @Date 2020/6/10 16:00
 * @Version 1.0
 */
public class JavaCourse extends AbstractCourse{

}
package com.wf.bridge.demo.course;

/**
 * @ClassName PythonCourse
 * @Description Python课程
 * @Author wf
 * @Date 2020/6/10 16:01
 * @Version 1.0
 */
public class PythonCourse extends AbstractCourse {
}

此时,系统类图所示:

8.2.2.3.桥接模式具体案例实现二
1.系统需求分析

  在我们日常的办公过程中,存在大量的消息同步,发送问题。

  发送消息,可能采取不同的方式,如:邮件,短信,系统内消息

  消息的类型,有不同:普通消息,加急消息,特急消息。

如下图示:

 2.代码实现

【1】定义消息顶层接口

package com.wf.bridge.demo.message;

/**
 * @ClassName IMessage
 * @Description 顶层消息接口
 * @Author wf
 * @Date 2020/6/10 16:33
 * @Version 1.0
 */
public interface IMessage {
    void send(String content, String user);
}

【2】消息发送方式子类实现

package com.wf.bridge.demo.message;

/**
 * @ClassName EmailMessage
 * @Description 邮件方式发消息
 * @Author wf
 * @Date 2020/6/10 16:35
 * @Version 1.0
 */
public class EmailMessage implements IMessage {
    @Override
    public void send(String content,String user) {
        System.out.println("使用邮件发送消息【"+content+"】,给【"+user+"】");
    }
}
ackage com.wf.bridge.demo.message;

/**
 * @ClassName SmsMessage
 * @Description 短信方式发送消息
 * @Author wf
 * @Date 2020/6/10 16:40
 * @Version 1.0
 */
public class SmsMessage implements IMessage{
    @Override
    public void send(String content, String user) {
        System.out.println("使用短信发送消息【"+content+"】,给【"+user+"】");
    }
}

【3】抽象引用消息实现

package com.wf.bridge.demo.message;

/**
 * @ClassName AbstractMessage
 * @Description 消息类型,抽象
 * @Author wf
 * @Date 2020/6/10 16:44
 * @Version 1.0
 */
public abstract class AbstractMessage {
    private IMessage message;

    public AbstractMessage(IMessage message) {
        this.message = message;
    }
    public void send(String content, String toUser){
        message.send(content,toUser);
    }
}

 【4】抽象消息修正

package com.wf.bridge.demo.message;

/**
 * @ClassName NormalMessage
 * @Description 普通消息
 * @Author wf
 * @Date 2020/6/10 16:50
 * @Version 1.0
 */
public class NormalMessage extends AbstractMessage {
    public NormalMessage(IMessage message) {
        super(message);
    }
}

package com.wf.bridge.demo.message;

import javax.sound.midi.Soundbank;

/**
 * @ClassName UrgencyMessage
 * @Description 加急消息
 * @Author wf
 * @Date 2020/6/10 16:47
 * @Version 1.0
 */
public class UrgencyMessage extends AbstractMessage {
    public UrgencyMessage(IMessage message) {
        super(message);
    }

    @Override
    public void send(String content, String toUser) {
        content += "[加急]";
        super.send(content, toUser);
        watch();
    }
    public void watch(){
        System.out.println("还需要做进度跟踪");
    }
}

【5】测试类

package com.wf.bridge.demo.message;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author wf
 * @Date 2020/6/10 16:51
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
       IMessage message = new SmsMessage();
       AbstractMessage abstractMessage = new NormalMessage(message);
       abstractMessage.send("加班申请","王总");

       //申请一直没回,比较着急了,需要加急
        message = new EmailMessage();
        abstractMessage = new UrgencyMessage(message);
        abstractMessage.send("加班申请","王总");
    }
}

测试结果如下:

 【6】系统类图

 8.2.3.桥接模式在源码中应用

8.2.3.1.jdbc连接

  java中连接数据库,底层通过jdbc来实现。

  java只是提供了数据库连接的抽象,具体实现由数据库厂商来实现【如:mysql,oracle...】

  所以,这里就用到了桥接模式。

1.测试类
package com.wf.bridge.demo.jdbc;

import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

/**
 * @ClassName Test
 * @Description TODO
 * @Author wf
 * @Date 2020/6/10 17:03
 * @Version 1.0
 */
@Slf4j
public class Test {
    public static void main(String[] args) {
        try {
            //1.加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接Connection实例
            //主机号:端口号/数据库名
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test3","root","root");
            Statement stmt = connection.createStatement();
            ResultSet rs = stmt.executeQuery("select * from dsg_red_list_info");
            System.out.println(rs);

        }catch (Exception e){
            log.error("数据库操作失败:{}",e.getMessage());
        }
    }
}
2.桥接模式应用
package com.mysql.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

为什么要实现这个java.sql.Driver呢?

在测试类,通过Class.forName找到Driver之后,首先,会加载静态块。

由DriverManager封装mysql的Driver实例,并存储到list中。

第二步,由DriverManager获取Connection实例。

DriverManage起到桥梁作用。将java的Driver,Connection和数据库厂商建立建立连接。

8.2.4.桥接模式的总结

8.2.4.1.优,缺点总结

优点:

  》分离抽象部分及其具体实现部分

  》提高了系统的扩展性

  》符合开闭原则

  》符合合成复用原则

缺点:

  1.增加系统的理解与设计难度

  2.需要正确地识别系统中两个独立变化的维度

8.2.4.2.桥接模式相关设计模式
1.桥接模式与组合模式

桥接模式:关注组合引用

组合模式:关注共同功能,根据主线组合

2.桥接模式与适配器模式

适配器:根据目标类,进行扩展,提高灵活性

桥接模式:必须有约定前提,并且严格按照约定实现。

原文地址:https://www.cnblogs.com/wfdespace/p/13026710.html