适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

  适配器模式的一些其他名称:变压器模式、转换器模式、包装(Wrapper)模式。适配器模式可以用于增加新的方法,但是,其主要意图是转换接口。

1.  现实生活中的适配器模式

  连接口径不一致的两根水管问题。假设两根水管,一根粗一根细,为了将这两根水管连接到一起,就需要用一个连接器将两个管子连接起来,如下:

    

2.适配器模式与概念

  适配器模式有类的适配器和对象的适配器模式。如下图:

    

2.1  类的适配器模式

  类的适配器模式把适配的类的API转换成为目标类的API,其静态结构如下:

    

  从图中看出,Adaptee没有option()方法,而客户端期待这个方法。因此采用一个适配器Adapter,继承Adaptee并且实现Target接口,在Adapter中实现option方法,并且重用Adaptee的option2方法。由于Adapter继承Adaptee,所以为类适配器模式。

角色有:

目标(Target)角色:期待的接口,由于是类适配器模式,因此目标不可以是类。

源(Adaptee)角色:现有需要适配的接口。

适配器(Adapter)角色:适配器是本模式的核心。把原接口转换成目标接口。这一角色必须是类,不可以是接口。

代码如下:

package cn.qlq.adapter;

public interface Target {

    void option();

    void option2();
}
package cn.qlq.adapter;

public class Adaptee {

    public void option2() {
System.out.println("option2");
}
}
package cn.qlq.adapter;

public class Adapter extends Adaptee implements Target {

    @Override
public void option() {
System.out.println("option");
} }

效果:使用一个具体类(Adapter)把源接口适配到目标(Target)中。这一依赖,如果源以及源的子类都使用此类适配就行不通了。

  由于适配器类是源的子类,因此可以在适配器类覆盖源的一些方法。

2.2 对象的适配器模式

  与类适配器不同的是,此模式不使用继承,而是使用委派关系连接到Adaptee类。其结构图如下:

  

  从图中看出,Adaptee没有option()方法,而客户端期待这个方法。因此采用一个适配器Adapter,实现Target接口。Adapter内部将option2任务委派给Adaptee,自己实现option方法。

角色有:

目标(Target)角色:期待的接口,目标可以是具体或抽象的类。

源(Adaptee)角色:现有需要适配的接口。

适配器(Adapter)角色:适配器是本模式的核心。把原接口转换成目标接口。这一角色必须是类,不可以是接口。

代码如下:

package cn.qlq.adapter;

public interface Target {

    void option();

    void option2();
}
package cn.qlq.adapter;

public class Adaptee {

    public void option2() {
System.out.println("option2");
}
}
package cn.qlq.adapter;

public class Adapter implements Target {

    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
super();
this.adaptee = adaptee;
} @Override
public void option() {
System.out.println("option");
} @Override
public void option2() {
adaptee.option2();
} }

效果:

(1)一个适配器可以把不同的源适配到同一个Target,换言之,同一个适配器可以把源类以及它的子类都适配到目标接口。

(2)与类的适配器模式相比,想要更换源类的方法就不容易。增加一些新的方法很容易。

2.3  类适配器模式与对象适配器模式区别

  对象适配器模式可以把多种不同的源适配到同一个Target,而类的适配器模式很难实现。

  如果一个被适配源类有大量的方法,使用类适配器模式比价容易(只需要Adapter类继承Adaptee即可)。 

2.4  适配器模式适用场景:

1.  系统需要使用现有的类,而此类的接口不符合系统的需要。

2.  想要建立一些可以重复使用的类,使得本来接口不相容并且无关的类结合在一起工作。

3.  在设计中需要改变多个子类接口,在作用相同但名称不同的类或方法之间进行适配时。

3.   缺省适配器模式

  缺省适配(Default Adapter)模式为一个接口提供缺省实现,这样子类型可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。作为适配器模式的一个特例,缺省适配模式在JAVA语言中有着特殊的应用。

  其实主要就是为Target提供一个默认的实现方法(该实现一般为abstract类,因为其实例化是没有意义的,用于在子类中扩展所需方法)。(JDK8之前不允许接口有实现方法,JDK8可以用default关键字声明方法)。

鲁智深的故事

  和尚要做什么呢?吃斋、念经、打坐、撞钟、习武等。如果设计一个和尚接口,给出所有的和尚都需要实现的方法,那么这个接口应当如下:

public interface 和尚 {
public void 吃斋(); public void 念经(); public void 打坐(); public void 撞钟(); public void 习武(); public String getName();
}

  显然,所有的和尚类都应当实现接口所定义的全部方法,不然就根本通不过JAVA语言编辑器。像下面的鲁智深类就不行。

public class 鲁智深 implements 和尚{
public void 习武(){
}
public String getName(){
return "鲁智深";
}
}

   由于鲁智深只实现了getName()和习武()方法,而没有实现任何其他的方法。因此,它根本就通不过Java语言编译器。鲁智深类只有实现和尚接口的所有的方法才可以通过Java语言编译器,但是这样一来鲁智深就不再是鲁智深了。以史为鉴,可以知天下。研究一下几百年前鲁智深是怎么剃度成和尚的,会对Java编程有很大的启发。不错,当初鲁达剃度,众僧说:“此人形容丑恶、相貌凶顽,不可剃度他",但是长老却说:”此人上应天星、心地刚直。虽然时下凶顽,命中驳杂,久后却得清净。证果非凡,汝等皆不及他。”

  原来如此!看来只要这里也应上一个天星的话,问题就解决了!使用面向对象的语言来说,“应”者,实现也;“天星”者,抽象类也。

public abstract class 天星 implements 和尚 {

    @Override
public void 吃斋() {
} @Override
public void 念经() {
} @Override
public void 打坐() {
} @Override
public void 撞钟() {
} @Override
public void 习武() {
} @Override
public String getName() {
return null;
} }

  鲁智深类继承抽象类“天星”

public class 鲁智深 extends 天星 {

    public void 习武() {
System.out.println("少林金刚圈");
} public String getName() {
return "鲁智深";
} }

  这个抽象的天星类便是一个适配器类,鲁智深实际上借助于适配器模式达到了剃度的目的。此适配器类实现了和尚接口所要求的所有方法。但是与通常的适配器模式不同的是,此适配器类给出的所有的方法的实现都是“平庸”的(也就是都是空方法,没有任何业务相关代码)。这种“平庸化”的适配器模式称作缺省适配模式。

  其类图如下:

  在很多情况下,必须让一个具体类实现某一个接口,但是这个类又用不到接口所规定的所有的方法。通常的处理方法是,这个具体类要实现所有的方法,那些有用的方法要有实现,那些没有用的方法也要有空的、平庸的实现。

  这些空的方法是一种浪费,有时也是一种混乱。除非看过这些空方法的代码,程序员可能会以为这些方法不是空的。即便他知道其中有一些方法是空的,也不一定知道哪些方法是空的,哪些方法不是空的,除非看过这些方法的源代码或是文档。

  缺省适配模式可以很好的处理这一情况。可以设计一个抽象的适配器类实现接口,此抽象类要给接口所要求的每一种方法都提供一个空的方法。就像帮助了鲁智深的“上应天星”一样,此抽象类可以使它的具体子类免于被迫实现空的方法。

  缺省适配器模式的核心是一个缺省适配器类,这个类应该是抽象类,因为这个类不应该被实例化,其实例化也是没意义的。但是抽象类的方法应该是具体的方法,而不是抽象方法,这些方法的存在就是为了提供默认的实现,以便缺省适配器的具体子类可以按需扩展相应方法。

  在任何时候,如果不准备实现一个接口的所有方法时,就可以使用“缺省适配模式”制造一个抽象类,给出所有方法的平庸(也就是空方法)的具体实现。这样,从这个抽象类再继承下去的子类就不必实现所有的方法了。

适配器(Adapter)模式的更多相关文章

  1. 设计模式--适配器(Adapter)模式

    今天学习另一个设计模式,适配器(Adapter)模式,这是一个共同方向,但有特殊要求,就应用到此设计模式.写到这里,想起很久以前,有写过一篇<ASP.NET的适配器设计模式(Adapter)&g ...

  2. 【原】模式之-适配器Adapter模式

    适配器Adapter模式 适配器模式(Adapter Pattern)把一个类的接口变换成客户端所期待的的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作. 模式所涉及的角色有 ...

  3. java演示适配器(adapter)模式

    为什么要使用模式: 模式是一种做事的一种方法,也即实现某个目标的途径,或者技术. adapter模式的宗旨就是,保留现有类所提供的服务,向客户提供接口,以满足客户的需求. 类适配器:客户端定义了接口并 ...

  4. Java 实现适配器(Adapter)模式

    平时我们会常常碰到这种情况,有了两个现成的类,它们之间没有什么联系.可是我们如今既想用当中一个类的方法.同一时候也想用另外一个类的方法.有一个解决方法是.改动它们各自的接口.可是这是我们最不愿意看到的 ...

  5. 设计模式C++描述----06.适配器(Adapter)模式

    一. 定义 适配器模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. Adapter 模式的两种类别:类模式和对象模式. 二. 举例说明 实际中 ...

  6. 2、适配器 adapter 模式 加个"适配器" 以便于复用 结构型设计模式

    1.什么是适配器模式? 适配器如同一个常见的变压器,也如同电脑的变压器和插线板之间的电源连接线,他们虽然都是3相的,但是电脑后面的插孔却不能直接插到插线板上. 如果想让额定工作电压是直流12伏特的笔记 ...

  7. 漫谈设计模式(一):代理(Proxy)模式与适配器(Adapter)模式对比

    1.前言 为什么要将代理模式与适配器模式放在一起来说呢?因为它们有许多的共同点,当然也有一些不同的地方.首先两者都是属于结构型模式.结构型模型是这样定义的: 结构型模式涉及到如何组合类和类以获得更大的 ...

  8. Adapter(适配器)模式

    1. 概述: 接口的改变,是一个需要程序员们必须(虽然很不情愿)接受和处理的普遍问题.程序提供者们修改他们的代码;系统库被修正;各种程序语言以及相关库的发展和进化.  例子1:iphone4,你即可以 ...

  9. 设计模式模式适配器(Adapter)摘录

    23种子GOF设计模式一般分为三类:创建模式.结构模型.行为模式. 创建模式抽象的实例,他们帮助建立一个系统,是独立于如何.这是一个这些对象和陈述的组合.创建使用继承一个类架构更改实例,一个对象类型模 ...

随机推荐

  1. C++中的Mat, const Mat, Mat &,Mat &, const Mat &的区别

    Mat, copy传递,不会改变外部变量的Mat. Mat &, reference传递,函数内部修改将会改变外部. const Mat, copy传递,在函数内,不会被修改,也不会影响到外部 ...

  2. 10. Javascript 前后端数据加密

    为了加强项目的接口安全程度,需求如下 var options = { // 前端需要传送的数据加密 data: { abc: 123, bcd: 123, cds: '撒旦教付货款12313', }, ...

  3. JSP JSTL

    JSTL是Sun给JSP制定的一套标准标签库,JS代表JSP,TL即Tag Library. JSTL是一套很古老的标签库了,很多东西都不再适用,这里只介绍几个常用的标签. 使用JSTL需下载添加以下 ...

  4. spoon数据转换中文乱码(kettle)

    (1) 查看mysql数据库是否为utf8(status) (2) 设置spoon (3) 文本打开spoon.bat,找到set OPT=%OPT% %PENTAHO_DI_JAVA_OPTIONS ...

  5. django 权限设置 左侧菜单点击显示,面包屑

    1.左侧菜单点击显示 就是在点击的时候保留点击的功能 方法. 1.加入新的字段,pid来判断 class Permission(models.Model): """ 权限 ...

  6. LCD编程_画点线圆

    上篇博客中进行了lcd的简单测试,这篇博客将进行更加复杂的测试——画点.画线.画圆.画线和画圆是在画点的基础上实现的,因此本篇博客重点实现画点操作. 先抛出这样的一个问题,已知: (x,y)的坐标: ...

  7. 201871010117-石欣钰《面向对象程序设计(java)》第二周学习总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...

  8. 前端html页面,手机查看

    在写前端页面中,经常会在浏览器运行HTML页面,从本地文件夹中直接打开的一般都是file协议,当代码中存在http或https的链接时,HTML页面就无法正常打开,为了解决这种情况,需要在在本地开启一 ...

  9. 从一段文字中提取出uri信息

    package handle.groupby; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io ...

  10. zabbix-钉钉报警媒介

    (1)第三方报警平台(钉钉) 先指定要发送的群,在群里创建机器人 添加机器人 可以参考   “说明文档” 创建测试文档 vim   ceshi.sh curl 'https://oapi.dingta ...