设计模式学习笔记(九):适配器模式

1 适配器模式

1.1 定义

将一个接口转换为客户但愿的另外一个接口,使接口不兼容的那些类能够一块儿工做,别名为包装器。
适配器中的接口是广义的接口,能够表示一个方法或者方法的集合。java

适配器模式既能够做为类结构型模式,也能够做为对象结构型模式。ide

1.2 分类

根据适配器与适配者类的关系不一样,能够分为对象适配器模式以及类适配器模式。测试

1.2.1 对象适配器模式

对象适配器模式就是适配器与适配者之间是关联关系
结构图以下:
在这里插入图片描述spa

1.2.2 类适配器模式

类适配器模式就是适配器与适配者之间是继承或实现关系。
结构图以下:
在这里插入图片描述
因为语言特性的限制,好比Java,C#不支持多重继承,类适配器模式受到不少限制,例如Target若是不是接口而是一个类,就没法使用类适配器模式。此外若是适配者为final类也没法使用适配器模式,在Java等语言中大部分状况下使用对象适配器模式。设计

1.3 角色

  • Target(目标抽象类):目标抽象类定义客户所需的接口,能够是一个抽象类或接口,也能够是一个具体类
  • Adapter(适配器类):适配器能够调用另外一个接口,做为一个转换器,对Adaptee和Target进行适配。适配器类是适配器模式的核心,在对象适配器模式中,它经过继承Target并关联一个Adaptee对象使二者产生联系
  • Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口须要适配,适配者类通常是一个具体类,包含了客户但愿使用的业务方法,在某些状况下可能没有适配者类的源代码

2 实例

2.1 对象适配器

Target类以及实现了Target的类:code

interface Target
{
    void request();
}

class ConcreteTarget implements Target
{
    @Override
    public void request()
    {
        System.out.println("具体Target方法");
    }
}

适配者类:对象

class Adaptee
{
    public void specificRequest()
    {
        System.out.println("Adaptee方法");
    }
}

适配器类(实现了Target,适配者做为成员变量):blog

class Adapter implements Target
{
    private Adaptee adaptee = new Adaptee();
    @Override
    public void request()
    {
        adaptee.specificRequest();
    }
}

测试:继承

public class Test
{
    public static void main(String[] args) 
    {
        Target target = new ConcreteTarget();
        target.request();
        Target adapter = new Adapter();
        adapter.request();
    }
}

2.2 类适配器

在上述对象适配器的基础上,适配者与Target保持不变,适配器继承了适配者并实现了Target,同时取消了适配者做为成员变量,在方法内直接调用super.xxx,也就是适配者的方法:接口

class Adapter extends Adaptee implements Target
{
    @Override
    public void request()
    {
        super.specificRequest();
    }
}

2.3 Micro USB与Type-C

假设目前只有一条Micro USB线以及一台只有Type-C接口的手机,须要对其进行充电,这时候就须要一个转接头把Micro USB转为Type-C接口,才能给手机充电。
这里的Target就是Type-C,适配者就是Micro USB,适配器就是转接头,简化实现代码以下:

public class Test
{
    public static void main(String[] args) {
        TypeC typeC = new MicroUSBToTypeC();
        typeC.chargeWithTypeC();
    }
}

//Target:给TypeC接口的手机充电
interface TypeC
{
    void chargeWithTypeC();
}

//Adaptee:适配者,MicroUSB线
class MicroUSB
{
    public void chargeWithMicroUSB()
    {
        System.out.println("MicroUSB充电");
    }
}

//Adapter:适配器,MicroUSB到TypeC的转接头
class MicroUSBToTypeC implements TypeC
{
    private MicroUSB microUSB = new MicroUSB();
    @Override
    public void chargeWithTypeC()
    {
        microUSB.chargeWithMicroUSB();
    }
}

3 双向适配器

在对象适配器的使用过程当中,若是在适配器中同时包含对Target类和Adaptee类的引用,Adaptee类能够经过适配器调用Target类中的方法,Target类也能够经过适配器调用Adaptee类的方法,那么该适配器就是一个双向适配器。例子以下:

public class Test
{
    public static void main(String[] args) {
        Adapter adapter = new Adapter();
        adapter.request();
        adapter.specificRequest();
    }
}

//适配者
interface Adaptee
{
    void specificRequest();
}

//Target类
interface Target
{
    void request();
}

//Target实现
class TargetImpl implements Target
{
    @Override
    public void request()
    {
        System.out.println("Target方法");
    }
}

//适配者实现
class AdapteeImpl implements Adaptee
{
    @Override
    public void specificRequest()
    {
        System.out.println("Adaptee方法");
    }
}

//适配器
class Adapter implements Adaptee,Target
{
    private Target target = new TargetImpl();
    private Adaptee adaptee = new AdapteeImpl();
    @Override
    public void request()
    {
        //Target的方法调用适配者方法
        adaptee.specificRequest();
    }

    @Override
    public void specificRequest()
    {
        //适配者方法调用Target的方法
        target.request();        
    }
}

4 缺省适配器

4.1 定义

缺省适配器:当不须要实现一个接口所提供的全部方法时,可先设计一个抽象类实现该接口,并为接口中的每一个方法都提供一个默认实现(空实现),那么该抽象类子类能够选择性覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中全部方法的状况,又叫单接口适配器模式。

4.2 结构图

在这里插入图片描述

4.3 角色

  • ServiceInterface(适配者接口):一般是一个声明了大量方法的接口
  • AbstractServiceClass(缺省适配器类):缺省适配器模式的核心类,使用空方法的形式实现了在ServiceInterface接口中声明的方法,一般定义为抽象类
  • ConcreteServiceClass(具体业务类):是缺省适配器类的子类,只须要有选择性地覆盖适配器者中定义的方法,其余的方法在缺省适配器类中提供了空实现

4.4 实例

Java AWT中通常能够经过两种方式来处理窗口事件:

  • 实现WindowListener
  • 继承WindowAdapter

其中WindowAdapter实现了WindowListener接口,可是都是提供了空实现,也就是说实现WindowsListener的话须要实现里面全部的方法,而继承WindowAdapter只须要选择性地覆盖方法便可,结构图:
在这里插入图片描述

5 主要优势

类适配器以及对象适配器的共同优势以下:

  • 解耦:将Target与Adaptee解耦,引入适配器来重用现有的适配者类,无须修改原有结构
  • 提升复用性:将具体的业务实现过程封装在适配者类中,对于客户端而言是透明的,并且提升了适配者类的复用性,同一个适配者类能够在多个不一样的系统复用
  • 扩展性好:能够很方便地更换适配器,也能够在不修改代码的基础上增长了新的适配器类,彻底符合开闭原则,扩展灵活

类适配器的独有优势以下:

  • 因为适配器类是适配者的子类,所以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

对象适配器的独有优势以下:

  • 一个对象适配器能够把多个不一样的适配者适配到同一个Target
  • 能够适配一个适配者的子类,因为适配器与适配者之间是关联关系,根据LSP(里氏代换原则),适配者的子类也能够经过该适配器进行适配

6 主要缺点

类适配器缺点:

  • 对于Java,C#等不支持多重继承的语言,一次最多只能适配一个适配者类
  • 适配者不能是“不能继承的类”,好比Java的final类,C#的sealed
  • 在Java,C#等Target只能是接口不能是类

对象适配器缺点:

  • 置换麻烦:相比起类适配器,在适配器中置换适配者的某些方法比较麻烦,须要先建立一个适配者类的子类,在子类将适配者类的方法置换掉,再把适配者的子类做为真正的适配者类进行适配,实现较为复杂

7 适用场景

  • 系统须要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需求,甚至没有这些类的源代码
  • 想建立一个能够重复使用的类,用于与彼此之间没有太大关联的类,包括可能在未来引进的类一块儿工做

8 总结

在这里插入图片描述

相关文章
相关标签/搜索