适配器模式

91 阅读4分钟

适用场景

1,系统需要使用现有的类,但现有的类却不兼容。

2,需要建立一个可以重复使用的类,用于一些彼此关系不大的类,并易于扩展,以便于面对将来会出现的类。

3,需要一个统一的输出接口,但是输入类型却不可预知。

适配器模式中一共有3个角色,分别是:源角色、目标角色、适配器角色;

类适配器模式

类适配器模式是通过继承来实现适配功能的,代码如下:

我们先来定义一个源角色(即220V电压)

/**
 * 类 名: AC220V
 * 描 述: TODO  源角色
 * 作 者: 黄加耀
 * 创 建: 2019/2/11 : 19:30
 * 邮 箱: huangjy19940202@gmail.com
 *
 * @author: jiaYao
 */
public class AC220V {
    /**
     * 获取220V电压
     *
     * @return
     */
    public int output220v() {
        int output220v = 220;
        return output220v;
    }

}

然后我们再来定义一个目标角色(即通过适配从而获取到的5V电压),这个地方我们定义的是一个接口

/**
 * 类 名: DC5V
 * 描 述: TODO 目标角色
 * 作 者: 黄加耀
 * 创 建: 2019/2/11 : 19:32
 * 邮 箱: huangjy19940202@gmail.com
 *
 * @author: jiaYao
 */
public interface DC5V {

    int dc5v();

}

然后,我们再来定义一个适配器,适配器的作用是将220V电压转换成为5V电压。我们看看适配器的代码

/**
 * 类 名: Adapter
 * 描 述: TODO 适配器角色
 * 作 者: 黄加耀
 * 创 建: 2019/2/11 : 19:33
 * 邮 箱: huangjy19940202@gmail.com
 *
 * @author: jiaYao
 */
public class Adapter extends AC220V implements DC5V {

    @Override
    public int dc5v() {
        int output220v = output220v();
        return (output220v / 44);
    }
}

这里,我们的适配器,继承于源角色并且实现目标角色,这样通过实现目标角色中的方法调用源角色中的方法进行运算,从而达到适配的效果。我们编写一个测试类看看

public class Test {
    /**
     * 基于类的适配器模式
     */
    @org.junit.Test
    public void test(){
        DC5V dc5V =  new Adapter();
        int dc5 = dc5V.dc5v();
        System.out.println("转换后的电压为:" + dc5 + " 伏...");
    }
}

从代码中我们可以看到,其实适配器做的主要工作就是为了让目标角色的API可以调用到源角色的API,适配器在中间做的是一个类似的中转作用,并且不影响源角色和目标角色原有的功能和逻辑。

由于java只支持单继承,所以这里我们的目标角色定义的是一个接口。下面我们再来看看对象适配器

对象适配器

对象适配器是通过组合来实现适配器功能的,即适配器拥有源角色的实例,我们使用代码来看看:

此处源角色和目标角色两个类代码和上面是一样的,不另做介绍,我们看看适配器角色代码

/**
 * 类 名: Adapter
 * 描 述: TODO 适配器角色
 * 作 者: 黄加耀
 * 创 建: 2019/2/11 : 19:33
 * 邮 箱: huangjy19940202@gmail.com
 *
 * @author: jiaYao
 */
public class Adapter implements DC5V {

    private AC220V ac220V;

    public Adapter(AC220V ac220V) {
        this.ac220V = ac220V;
    }

    @Override
    public int dc5v() {
        int output220v = ac220V.output220v();
        return (output220v / 44);
    }
}

如上代码所示,我们的适配器中有一个有参构造,参数为源角色对象实例,适配器中有源角色对象实例引用,通过对象的引用我们进行适配转换。我们编写一个测试类看看

public class Test {
    /**
     * 基于对象的适配器模式
     */
    @org.junit.Test
    public void test(){
        DC5V dc5V =  new Adapter(new AC220V());
        int dc5 = dc5V.dc5v();
        System.out.println("输入的电压为:" + new AC220V().output220v() + " 伏...");
        System.out.println("转换后的电压为:" + dc5 + " 伏...");
    }
}

如上所示,我们的测试类中在创建适配器时,传入参数为源角色实例,然后通过适配器将其转换为目标角色的API。

场景

兼容老版本接口

在做版本升级的时候,对于一些要废弃的接口,我们不直接将其删除,而是暂时保留,并且标注为 deprecated,并将内部实现逻辑委托为新的接口实现。这样做的好处是,让使用它的项目有个过渡期,而不是强制进行代码修改。这也可以粗略地看作适配器模式的一个应用场景。

举个例子,JDK1.0 中包含一个遍历集合容器的类 Enumeration。JDK2.0 对这个类进行了重构,将它改名为 Iterator 类,并且对它的代码实现做了优化。但是考虑到如果将 Enumeration 直接从 JDK2.0 中删除,那使用 JDK1.0 的项目如果切换到 JDK2.0,代码就会编译不通过。为了做到兼容使用低版本 JDK 的老代码,我们可以暂时保留 Enumeration 类,并将其实现替换为直接调用 Itertor。代码示例如下所示:

public class Collections {
  public static Emueration emumeration(final Collection c) {
    return new Enumeration() {
      Iterator i = c.iterator();

      public boolean hasMoreElments() {
        return i.hashNext();
      }

      public Object nextElement() {
        return i.next():
      }
    }
  }
}