适配器模式简介

适配器模式定义
  将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。简单的说就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。
适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
在业务开发中我们会经常的需要做不同接口的兼容,比如需要把各个业务线的各种类型服务做统一包装,再对外提供接口进行使用。而这在我们平常的开发中也是非常常见的。
根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。

适配器模式包含以下角色:
目标抽象类:Target,该角色把其他类转换为我们期望的接口,可以是一个抽象类或接口,也可以是具体类。定义Client使用的与特定领域相关的接口
被适配者: Adaptee ,原有的接口,也是希望被适配的接口。定义一个已经存在的接口,这个接口需要适配,需要适配别人(要适配者)
适配器: Adapter, 将被适配者和目标抽象类组合到一起的类。对Adaptee的接口与Target接口进行适配

适配器模式优缺点:
优点:
1、将目标类和适配者类解耦
2、增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性
3、灵活性和扩展性都非常好,符合开闭原则

缺点:
1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。
2、 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

使用场景
(1)系统需要使用现有的类,而此类的接口不符合需要。
(2)需要一个统一的输出接口,而输入类型不可预知。
(3)创建一个可以复用的类,使得该类可以与其他不相关的类或不可预见的类(协同工作。

适配器模式的实现
适配器模式有两种:“对象”适配器和“类”适配器。
对象适配器模式: 基本思路与类适配器模式相同, 只是将 Adapter适配器类修改, 不是继承 Adaptee被适配者, 而是拥有 Adaptee类的实例, 以解决兼容性的问题。
类适配器模式: Adapter适配器类, 继承 Adaptee被适配器类, 实现 Target目标类的接口, 完成适配。类适配器模式是通过让Adapter(适配器)实现Target(被适配者)的抽象接口,然后继承Adaptee(要适配者),具体适配过程是由我们的适配器的Resuest()方法中对Adaptee(要适配者)的SpecificRequest()方法进行适配,使得适配器的Request()方法返回我们需要的被适配者,供我们使用。

以下举一个类适配器的例子:
如:在生活中手机充电器, 需要将家用220V的交流电 转换为 5V的直流电后, 才能对手机充电

手机充电器 相当于 Adapter适配器
220V的交流电 相当于 Adaptee 被适配者
5V的直流电 相当于 Target目标

Adaptee被适配器类: 220V电压

/**
 * @author yyx
 */
public class Adaptee {
    public int output220V() {
        System.out.println("正常220V电压");
        return 220;
    }
}

Target目标接口: 输出5V电压

/**
 * @author yyx
 */
public interface Target {
    public int output5V();
}

Adapter适配器类: 将220V电压转换成 5V电压

/**
 * @author yyx
 */
public class Adapter extends Adaptee implements Target {
    @Override
    public int output5V() {
        // 获取到220V的电压
        int a = output220V();
        // 处理电压,转成5V
        int b = a / 44;
        return b;
    }
}

手机实体类

/**
 * @author yyx
 */
public class Phone {
    public void charging(Adapter voltage) {
        if (voltage.output5V() == 5) {
            System.out.println("电压适配为5V,可以充电");
        } else if (voltage.output5V() > 5) {
            System.out.println("电压大于5V,不能充电~");
        }
    }
}

测试Client:

/**
 * @author yyx
 */
public class Client {
    public static void main(String[] args) {
        System.out.println("类适配器模式");
        Phone phone = new Phone();
        phone.charging(new Adapter());
    }
}

运行结果如下所示:

类适配器模式
正常220V电压
电压适配为5V,可以充电

注意:
Java是单继承机制, Adapter类适配器需要继承 Adptee被适配者类, 导致 Target目标必须是接口, 有一定的局限性 (继承, 破坏了类的封装性, 父类对于子类来说是透明的, 且耦合度高)
由于继承 Adptee被适配者类, 所以可以根据需求重写 Adptee被适配者类的方法, 是的Adapter的灵活性增强了

** 对象适配器的例子:**
对象适配器模式: 基本思路与类适配器模式相同, 只是将 Adapter适配器类修改, 不是继承 Adaptee被适配者, 而是拥有 Adaptee类的实例, 以解决兼容性的问题.

修改Adapter适配器类, 将继承Adaptee类, 修改成拥有Adaptee类的成员属性
Adapter适配器类

/**
 * @author yyx
 */
//public class Adapter extends Adaptee implements Target {
//    @Override
//    public int output5V() {
//        // 获取到220V的电压
//        int a = output220V();
//        // 处理电压,转成5V
//        int b = a / 44;
//        return b;
//    }
//}

public class Adapter implements Target {
    private Adaptee adaptee;

    public Adapter() {
    }

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

    @Override
    public int output5V() {
        int dst = 0;
        if (adaptee != null) {
            int src = adaptee.output220V();
            System.out.println("使用对象适配器进行适配");
            dst = src / 44;
            System.out.println("适配完成,输出电压为:" + dst);
        }
        return dst;
    }
}

测试Client:

/**
 * @author yyx
 */
public class Client {
//    public static void main(String[] args) {
//        System.out.println("类适配器模式");
//        Phone phone = new Phone();
//        phone.charging(new Adapter());
//    }

    public static void main(String[] args) {

        System.out.println("对象适配器模式");

        Phone phone = new Phone();
        phone.charging(new Adapter(new Adaptee()));

    }
}

运行结果如下所示:

对象适配器模式
正常220V电压
使用对象适配器进行适配
适配完成,输出电压为:5
电压适配为5V,可以充电

注意:对象适配器模式相比于类适配器模式会更加灵活一些,我们的Adaptee是通过构造器传入的,这样可以更方便我们代码的扩展(多态),而且类适配器如果Target不是一个接口,也是一个类的话,Java中是不支持多继承的。

总而言之:
适配器模式的用意是要改变源的接口,以便于目标接口相容。缺省适配的用意稍有不同,它是为了方便建立一个不平庸的适配器类而提供的一种平庸实现。建议尽量使用对象的适配器模式,多用合成/聚合、少用继承。
总结:
1.从用户的角度看不到被适配者,是解耦的
2.用户直接调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
3.适配器实现了用户收到反馈结果,感觉只是和目标接口交互
4.适配器将一个对象包装起来用以改变其接口。
5.当需要使用一个现有的类而其接口并不符合你的需求时,可以使用适配器。
6.实现一个适配器可能会花一番功夫,可能也会毫不费力,原因是有其目标接口的大小与复杂度决定。


以上代码下载请点击该链接:https://github.com/Yarrow052/Java-package.git

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐