Java设计模式再相识 (七)——适配器模式

208 阅读5分钟

「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。

如果你想让额定工作电压12V的笔记本电脑在220V的家用交流电下工作,你会怎么做?通常,你不需要自己进行转换。买电脑时会配套一个电源适配器(AC Adapter),这个适配器的作用就是将220V的家庭电压转换为我们所需要的12V笔记本的电压。这样做笔记本才不会被烧坏。 在软件开发中,也经常会存在现有的程序无法直接进行使用,需要进行一些转换才能正常使用。这就引入了适配器(Adapter)模式。

适配器模式

适配器(Adapter)模式:Adapter模式也被称为Wrapper模式。Wrapper有包装、封装的意思。顾名思义,适配器模式通过一系列操作将我们要转换的对象进行封装,使其能够拥有其它用途。

适配器模式分为以下两种形式:

  • 类适配器模式
  • 对象适配器模式

下文我们将详细讲解这两种模式。

适配器模式的结构

适配器模式包含以下结构:

  1. 目标对象(Target):即业务所需要转换的最终形态。例如上文笔记本电脑需要的12V电源。
  2. 被适配者(Adaptee):被适配者对象是一个包含既定方法的对象。例如上文中的220V家庭用电。
  3. 适配器(Adapter):适配器模式的核心对象。它是一个转换器,能够通过继承或委托来把被适配者转换为目标对象。

实际应用场景

  • 第三方登录
  • 多数据源项目的适配

接下来我们来讲解类适配器模式。

类适配器模式

类适配器模式即使用继承的适配器模式。类适配器模式类之间的耦合度较高,这会导致框架的开发者需要了解现有框架中的相关实现类的内部结构,所以应用也较少。用得较多的是对象适配器模式(下文介绍)。

示例

本文,我们将使用适配器模式实现一个第三方登录的适配器。我们先使用类适配器来实现。

现在,我们的项目有一个需求,需要在原来账号密码登录的基础上,接入QQ和微信两种第三方登录方式。

我们先来使用类适配器实现。

首先,新建一个目标接口,这个接口中定义了我们需要的登录方法。

Target.java

package com.yeliheng.adapter.classadapter;

/**
 * 被适配的目标
 */
public interface Target {

    public void login();

}

我们定义了一个login()方法,这也是适配的最终对象。

接着,我们定义一个Adaptee,即将被适配的对象。

Adaptee.java

package com.yeliheng.adapter.classadapter;

/**
 * 被适配者
 */
public class Adaptee {
    public void wechatLogin() {
        System.out.println("微信登录成功,Token为xxx");
    }

    public void qqLogin() {
        System.out.println("qq登录成功,Token为xxx");
    }
}

因为我们最终需求需要适配成微信和QQ两种登录方式,所以被适配的对象是微信登录和QQ登录。我们在登录后返回token,这里简单地输出模拟这种场景。

然后我们就可以开始写适配器了。

LoginAdapter.java

package com.yeliheng.adapter.classadapter;

/**
 * 适配器
 */
public class LoginAdapter extends Adaptee implements Target{
    @Override
    public void login() {
        wechatLogin(); // 微信登录
    }
}

在登录适配器中,我们继承了Adaptee,即被适配者,也就是类适配器模式的写法,然后我们实现了Target目标类中的login()方法,实现了微信登录。

调用登录:

Main.java

package com.yeliheng.adapter.classadapter;

public class Main {
    public static void main(String[] args) {

        System.out.println("适配器测试:尝试微信登录...");

        Target target = new LoginAdapter();
        target.login();
    }
}

main函数中,我们实例化了一个LoginAdapter并赋值给Target目标类型。因为在适配器中,我们已经将不能直接使用的对象转换成我们可以使用的对象了。然后我们直接调用target中的login方法,即可实现第三方登录。具体业务场景中,我们需要判断用户行为选择登录的平台。

本例输出结果如下:

output.png

对象适配器模式

接下来,我们来继续讲解对象适配器模式。对象适配器模式是一种使用委托(delegate)的适配器模式。即:将某个方法中的实际处理逻辑移交给其他实例的方法进行处理。

示例

我们继续以第三方登录为例,实现对象适配器。

对象适配器的实现方式除了Adapter类,其他类均与类适配器相同。这里仅给出不同的类的源码。完整源代码参见Github

LoginObjectAdapter.java

package com.yeliheng.adapter.objectadapter;

public class LoginObjectAdapter implements Target{

    private Adaptee adaptee;

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

    @Override
    public void login() {
        adaptee.qqLogin();
    }
}

Main.java

package com.yeliheng.adapter.objectadapter;

public class Main {
    public static void main(String[] args) {
        System.out.println("适配器测试:尝试登录...");

        Adaptee adaptee = new Adaptee();
        Target target = new LoginObjectAdapter(adaptee);
        target.login();
    }
}

通过代码我们可以看出与类适配器模式不同的是,对象适配器不直接使用继承的方式实现适配,而是以构造函数注入的方式。这样做的好处在于:Java是单继承语言,若直接使用类适配器的继承,那么只能继承一种被适配者。如果我们需要灵活引入适配者,这样的方式就无法满足我们的需求。于是我们采用对象适配器,通过构造函数传入被适配者的对象,从而达到适配多种适配者的目的。

代码的输出结果如下:

output.png

适配器模式的优缺点

优点

  • 能够使现有的类得到复用:程序员无需修改代码即可使用现有的适配者类。
  • 降低项目耦合度:能够有效解决目标接口与现有接口的区别。
  • 版本升级的兼容性:软件升级往往不能对已经暴露的API进行修改,这时就可以使用适配器进行向下兼容,达到高版本兼容低版本的目的。

缺点

  • 过度使用适配器会增加系统的复杂性,降低代码的可读性。

总结

本文详细讲解了适配器模式的两种实现方式,分别是类适配器模式和对象适配器模式。并使用第三方登录作为例子实现了适配器。

本文例子的完整源码参见:Github