适配器模式

适配器模式

适配器模式介绍

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。举个简单的例子,现如今许多智能手机为了减小空间的浪费,逐渐取消了3.5mm圆孔的耳机孔,只保留了一个充电和插耳机通用的type-c孔,那么为了能让我们圆形耳机孔能适配type-c口,我们只需要买一个转接头就能将圆孔转换成type-c口就能正常使用圆孔耳机了。适配器适用于以下几种业务场景:

1、已经存在的类,它的方法和需求不匹配的情况。

2、适配器模式不是软件设计阶段考虑的设计模式,而是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。

适配器模式案例一

下面通过代码展示一下3.5mm圆形耳机插口通过适配器转成type-c的耳机插口,首先编写圆形插口耳机类CircleEarphone

/**
 * @author: Winston
 * @createTime: 2021/6/28
 *
 * 圆形耳机,表示只能插圆形的手机
 */
public class CircleEarphone {

    public void insertCircle(){
        System.out.println("圆形耳机插头,能插入圆形耳机孔");
    }

}

目标接口TypeC,也就是客户想要的功能

public interface TypeC {

    /**
     * 能插入type-c耳机孔的头
     */
    public void insertTypeC();
}

圆形插头的适配器Adapter,也就是把圆形插头转成type-c插头的适配器,这里有两种形式实现,第一种是类适配器,第二种是方法适配器。

/**
 * @author: Winston
 * @createTime: 2021/6/28
 *
 * 圆形耳机的适配器实现1,类适配器,通过集成和实现接口的方式,将圆形插头->type-c插头
 */
public class CircleAdapter1 extends CircleEarphone implements TypeC {

    @Override
    public void insertTypeC() {
        // 调用父类方法
        insertCircle();
        System.out.println("使用圆口适配器,将圆形耳机插口转成type-c耳机插口");
    }
}
/**
 * @author: Winston
 * @createTime: 2021/6/28
 *
 * 圆形耳机的适配器实现二,方法适配器,通过构造方法的形式,将圆形插头->type-c插头
 */
public class CircleAdapter2 implements TypeC {
    // 直接关联被适配类
    private CircleEarphone circleEarphone;

    // 通过构造函数传入具体需要适配的被适配类对象
    public CircleAdapter2(CircleEarphone circleEarphone) {
        this.circleEarphone = circleEarphone;
    }

    @Override
    public void insertTypeC() {
        // 这里是使用委托的方式完成特殊功能
        circleEarphone.insertCircle();
        System.out.println("使用圆口适配器,将圆形耳机插口转成type-c耳机插口");
    }
}

编写测试类typc-c耳机插口的手机:

/**
 * @author: Winston
 * @createTime: 2021/6/28
 *
 * 测试类,type-c耳机口的手机
 */
public class TypeCMobile {
    public static void main(String[] args) {
        System.out.println("==================第一种适配器:类适配器=====================");
        CircleAdapter1 adapter1 = new CircleAdapter1();
        adapter1.insertTypeC();
        System.out.println("我是type-c耳机孔的智能手机,插入成功");

        System.out.println("==================第二种适配器:方法适配器=====================");
        CircleAdapter2 adapter2 = new CircleAdapter2( new CircleEarphone());
        adapter2.insertTypeC();
        System.out.println("我是type-c耳机孔的智能手机,插入成功");
    }
}

运行结果:

==================第一种适配器:类适配器=====================
圆形耳机插头,能插入圆形耳机孔
使用圆口适配器,将圆形耳机插口转成type-c耳机插口
我是type-c耳机孔的智能手机,插入成功
==================第二种适配器:方法适配器=====================
圆形耳机插头,能插入圆形耳机孔
使用圆口适配器,将圆形耳机插口转成type-c耳机插口
我是type-c耳机孔的智能手机,插入成功

我们来看一下适配器模式的结构图:

适配器模式结构图.png

在上面这个案例中,TypeCMobile就是途中的Client,TypeC就是客户想要的目标接口Target,它可以是具体或抽象的类,CircleEarphone就是源接口也就是适配者Adaptee,CircleAdapter1(CircleAdapter2)就是适配器,通过适配器将源接口转成目标接口。

适配器模式案例二

在以前的老系统的登录接口都是通过账号和密码登录,现如今单纯的只靠账号和密码登录已经不满足用户的需求了,现在大多数的网站都支持第三方登录,比如QQ登录、微信登录、手机号登录等。现在我们将老系统的登录接口进行扩展,增加多种登录方式,虽然说登录的形式丰富了,但是登录以后的逻辑并不需要修改。先创建一个返回结果ResultMsg

public class ResultMsg {

    /**
     * 状态码
     */
    private int code;

    /**
     * 结果信息
     */
    private String msg;

    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;
    }
}

假设老系统的登录逻辑 SignInService

/**
 * @author: Winston
 * @createTime: 2021/6/29
 *
 * 老系统原来的登录Service
 */
public class SigInService {

    /**
     * 注册方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg register(String username, String password){
        return new ResultMsg(200,"注册成功");
    }

    /**
     * 登录方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username, String password){
        return new ResultMsg(200,"登录成功");
    }
}

已有的类不做修改,我们通过创建新的类来继承SignInService完成新登录方式的添加

/**
 * @author: Winston
 * @createTime: 2021/6/29
 *
 * 第三方登录方式
 */
public class SignInForThirdService extends SignInService {

    public ResultMsg loginByQQ(String openId){
        System.out.println("通过QQ登录");
        return new ResultMsg(200,"使用QQ登录成功");
    }

    public ResultMsg loginByWechat(String openId){
        System.out.println("通过微信登录");
        return new ResultMsg(200,"使用微信登录成功");
    }

    public ResultMsg loginByMobile(String mobile,String code){
        System.out.println("通过手机号登录");
        return new ResultMsg(200,"使用手机号登录成功");
    }
}

测试:

public class SignInForThirdServiceTest {
    public static void main(String[] args) {
        String openId = "adjkfjsdo2lkjdsf";
        SignInForThirdService signInForThirdService = new SignInForThirdService();
        ResultMsg resultMsg = signInForThirdService.loginByWechat(openId);
        System.out.println(resultMsg.getCode()+"=======>"+resultMsg.getMsg());
    }
}

SignInForThirdService类通过继承SignInService就能不改变原有逻辑同时能够进行登录功能的扩展完成简单的适配,当然我们可以更进一步根据不同的登录方式,创建不同的适配器Adapter,我们将登录案例重新进行优化。

首先是我们原来的登录接口

/**
 * @author: Winston
 * @createTime: 2021/6/29
 *
 * 老系统原来的登录Service
 */
public class SignInService {

    /**
     * 注册方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg register(String username, String password){
        return new ResultMsg(200,"注册成功");
    }

    /**
     * 登录方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username, String password){
        return new ResultMsg(200,"登录成功");
    }
}

接下来将我们要实现的几种第三方登录方式放在一个接口中,也就是我们要扩展的那几种方式。

/**
 * 只扩展
 * Created by Tom on 2019/3/16.
 */
public interface ISignInForThird {

    /**
     * QQ登录
     * @param id
     * @return
     */
    ResultMsg loginForQQ(String id);

    /**
     * 微信登录
     * @param id
     * @return
     */
    ResultMsg loginForWechat(String id);


    /**
     * 手机号登录
     * @param mobile
     * @param code
     * @return
     */
    ResultMsg loginForMobile(String mobile, String code);

}

编写适配器类SignInForThirdAdapter,同样也是继承原来的Service类SignInService同时实现未实现的接口ISignInForThird

/**
 * @author: Winston
 * @createTime: 2021/6/29
 */
public class SignInForThirdAdapter extends SignInService implements ISignInForThird {

    @Override
    public ResultMsg loginForQQ(String id) {
        return null;
    }

    @Override
    public ResultMsg loginForWechat(String id) {
        return null;
    }

    @Override
    public ResultMsg loginForMobile(String mobile, String code) {
        return null;
    }
}

SignInForThirdAdapter类是为了适配ISignInForThird接口中的几种登录方式,是个大的适配器,我们可以分别为不同的登录方式单独创建适配器,在这里不论是QQ登录适配器、还是微信登录适配器或者是手机号登录适配器它们都有通用方法,因此我们创建一个LoginAdapter接口,所有第三方适配器都可以实现该接口,当然这一步并不是必须的,这个接口可有可无。

/**
 * @author: Winston
 * @createTime: 2021/6/29
 *
 * 第三方登录适配器
 */
public interface LoginAdapter {
    

    /**
     * 登录方法
     * @param params  登录需要的参数
     * @return
     */
    ResultMsg login(String... params);

}

紧接着我们分别写QQ登录适配器、微信登录适配器和手机登录适配器

/**
 * @author: Winston
 * @createTime: 2021/6/29
 *
 * QQ登录适配器
 */
public class QQLoginAdapter implements LoginAdapter{


    /**
     * 使用QQ登录方法
     * @param params
     * @return
     */

    @Override
    public ResultMsg login(String... params){
        System.out.println("使用QQ账号登录系统");
        return new ResultMsg(200,"使用QQ账号登录系统成功");
    }
}

/**
 * @author: Winston
 * @createTime: 2021/6/29
 * <p>
 * 微信登录适配器
 */
public class WechatLoginAdapter implements LoginAdapter {

    /**
     * 使用微信登录方法
     *
     * @param params
     * @return
     */
    @Override
    public ResultMsg login(String... params) {
        System.out.println("使用微信账号登录系统");
        return new ResultMsg(200,"使用微信账号登录系统成功");
    }
}
/**
 * @author: Winston
 * @createTime: 2021/6/29
 * <p>
 * 手机号登录适配器
 */
public class MobileLoginAdapter implements LoginAdapter {
    /**
     * 使用手机号验证码登录方法
     *
     * @param params
     * @return
     */
    @Override
    public ResultMsg login(String... params) {
        System.out.println("使用手机号和验证码登录系统");
        return new ResultMsg(200,"使用手机号验证码登录系统成功");
    }
}

将SignInForThirdAdapter中的方法补充完整

/**
 * @author: Winston
 * @createTime: 2021/6/29
 */
public class SignInForThirdAdapter extends SignInService implements ISignInForThird {

    @Override
    public ResultMsg loginForQQ(String id) {
        return this.loginProcess(QQLoginAdapter.class,id);
    }


    @Override
    public ResultMsg loginForWechat(String id) {
        return this.loginProcess(WechatLoginAdapter.class,id);
    }

    @Override
    public ResultMsg loginForMobile(String mobile, String code) {
        return this.loginProcess(MobileLoginAdapter.class,mobile,code);
    }


    /**
     * 登录流程,这里用到了简单工厂模式及策略模式
     * @param loginAdapterClass  适配器class
     * @param params 参数
     * @return
     */
    private ResultMsg loginProcess(Class<? extends LoginAdapter> loginAdapterClass, String... params) {
        // 首先根据class查找合适的适配器类型
        try {
            LoginAdapter adapter = loginAdapterClass.newInstance();
            return adapter.login(params);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }


        return new ResultMsg(500,"登录失败");
    }
}

适配器模式小结

优点:

  1. 可以让任何两个没有关联的类一起运行
  2. 提高了类的复用,现有的类复用不需要改变

缺点:

  1. 过多的使用适配器会让系统非常凌乱
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容