Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >设计模式02——Adapter模式

设计模式02——Adapter模式

作者头像
itlemon
发布于 2020-04-03 10:06:19
发布于 2020-04-03 10:06:19
53400
代码可运行
举报
文章被收录于专栏:深入理解Java深入理解Java
运行总次数:0
代码可运行
定义

适配器设计模式,顾名思义就是将适配器的作用总结抽象成为一种代码的组织方式,将现有的代码通过适配器进行适配,以满足项目对另外一个类或者接口的要求。换句话说就是将一个类的接口适配(包装/转换)成客户(调用者)希望的另一个接口。适配器设计模式有以下两种形式:

  • 类适配器模式(使用继承的适配器)
  • 对象适配器模式(使用委托的适配器)
问题引入

我们常用的笔记本电脑的配件中就有一个适配器,负责将220V交流电转换成为12V的直流电给笔记本电脑供电,它存在的作用就是将220V交流电转换成为12V的直流电。所以它就是适配器,220V交流电就是被适配的对象,而12V直流电就是转换后的目标对象,笔记本电脑就是这个目标对象的调用者。

适配器设计模式在JDK源码中的应用

学习适配器设计模式,当然也需要从JDK中去寻找它的踪迹,在JDK源码中,采用适配器设计模式的地方很多,比如最常见的IO转换流和集合等。接下来我们一起从源码中来分析适配器设计模式是如何使用起来的。 我们一起阅读一下java.io.InputStreamReader(InputStream)的部分源码:

InputStreamReader的作用是将字节流转换为字符流,是它们之间转换的桥梁(适配器),也就是说,InputStreamReader就是适配器,负责将InputStream转换为Reader,这样就可以使用Reader的方法来执行各项操作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package java.io;

import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import sun.nio.cs.StreamDecoder;

public class InputStreamReader extends Reader {

    private final StreamDecoder sd;
    
    public InputStreamReader(InputStream in) {
        super(in);
        try {
            sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
        } catch (UnsupportedEncodingException e) {
            throw new Error(e);
        }
    }
    
    public String getEncoding() {
        return sd.getEncoding();
    }

    public int read() throws IOException {
        return sd.read();
    }

    public int read(char cbuf[], int offset, int length) throws IOException {
        return sd.read(cbuf, offset, length);
    }

    public boolean ready() throws IOException {
        return sd.ready();
    }

    public void close() throws IOException {
        sd.close();
    }
}

上面的代码是经过删减后的部分代码,删除了源码中的注释以及几个构造方法,从阅读源码来看,这个适配器设计模式的形式采用的是“对象适配器模式”,至于何是“对象适配器模式”,我们将在后面的学习中介绍,读者可以暂时不必理会何是“对象适配器模式”。

手动实现适配器设计模式

接下来,我们将手动实现两种适配器设计模式,用简单的代码来说明适配器设计模式是如何运转起来的。

以前小时候都玩过QQ游戏,有时候需要充值Q币,使用Q币在游戏中购买道具,这种情景就可以完全适用适配器设计模式,那么在这种情景中,使用Q币来购买游戏道具是我们需求,也就是我们的目标(Target),而现在的现状是我们有人民币,那么人民币就是被适配的对象(Adaptee),由于人民币不能直接在游戏中购买道具,它需要被转换成Q币才可以进行交易,所以我们还需要一个适配器(Adapter),负责将人民币转换成Q币。 根据以上的文字,我们可以将其总结成为一个表格,可以很方便地理清关系:

角色

扮演者

作用

Target

购买游戏道具的接口

有充值Q币接口,有使用Q币充值游戏道具的接口

Adapter

Q币充值器

将人民币转换成为Q币,并完成充值

Adaptee

人民币

被适配,被转换的对象

根据以上的关系,我们分别来创建各个角色对应的类或者接口。这里我们模拟了使用人民币充值Q币,使用Q币购买游戏道具的案例,假设一个单位的人民币可以充值10Q币,每个Q币可以一个游戏道具。

示例代码1:类适配器设计模式(使用继承的适配器)
  • Target

我们的目标是有一个接口,这个接口可以购买游戏道具,但是需要使用Q币来进行购买。在适配器设计模式里,它是我们需要最终的目标。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.itlemon.design.pattern.chapter02.adapter.example3;

/**
 * @author jiangpingping
 * @date 2018/9/6 下午7:43
 */
public interface TargetInterface {

    /**
     * 购买qCoinCount个游戏道具
     */
    void buyGameProps();

}
  • Adaptee

现在的现状是手头上有人民币,所以需要有一个Q币充值器,将人民币换成相同价值的Q币。在适配器设计模式里面,人民币就是需要被适配的对象,因为它不能直接用来购买游戏道具,但是得必须通过它才可以充值Q币,才能满足要求。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.itlemon.design.pattern.chapter02.adapter.example3;

/**
 * 人民币
 *
 * @author jiangpingping
 * @date 2018/9/6 下午7:45
 */
public class Rmb {

    private int count;

    public Rmb(int count) {
        this.count = count;
    }

    public int getCount() {
        return this.count;
    }

}
  • Adapter

所以我们需要一个Q币充值器,将人民币换成Q币,然后还具备购买游戏道具的功能。在适配器设计模式中,Q币充值器就是我们所需的适配器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.itlemon.design.pattern.chapter02.adapter.example3;

/**
 * Q币充值器
 *
 * @author jiangpingping
 * @date 2018/9/6 下午7:31
 */
public class QCoinRechargeableDevice extends Rmb implements TargetInterface {

    private int qCoinCount;

    public QCoinRechargeableDevice(int rmbCount) {
        super(rmbCount);
        this.qCoinCount = getCount() * 10;
    }

    @Override
    public void buyGameProps() {
        System.out.println("一共购买了" + qCoinCount + "个道具");
    }
}
  • Main

这里写一个Main方法,来验证我们上面设计的适配器设计模式,主要代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.itlemon.design.pattern.chapter02.adapter.example3;

/**
 * @author jiangpingping
 * @date 2018/9/6 下午9:26
 */
public class Main {

    public static void main(String[] args) {
        TargetInterface qCoinRechargeableDevice = new QCoinRechargeableDevice(10);
        qCoinRechargeableDevice.buyGameProps();
    }
}

这里,我们向Q币充值器充值10个单位的人民币,就可以完成购买100个游戏道具转变。本来人民币不能直接用来购买游戏道具,使用适配器设计模式之后,就可以完成我们购买游戏道具的需求。

使用继承的适配器UML类图

使用继承的适配器有一个特点就是Adapter继承了Adaptee,并实现了Target,这就是三者之间的关系。

示例代码2:对象适配器设计模式(使用委托的适配器)

这里仅仅是贴出代码,对于各个类的说明,在上面都已经进行了阐述。

  • Target
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.itlemon.design.pattern.chapter02.adapter.example4;

/**
 * @author jiangpingping
 * @date 2018/9/10 下午9:16
 */
public abstract class TargetInterface {

    /**
     * 购买qCoinCount个游戏道具
     */
    public abstract void buyGameProps();
}
  • Adaptee
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.itlemon.design.pattern.chapter02.adapter.example4;

/**
 * 人民币
 *
 * @author jiangpingping
 * @date 2018/9/6 下午7:45
 */
public class Rmb {

    private int count;

    public Rmb(int count) {
        this.count = count;
    }

    public int getCount() {
        return this.count;
    }

}
  • Adapter
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.itlemon.design.pattern.chapter02.adapter.example4;

/**
 * @author jiangpingping
 * @date 2018/9/10 下午9:18
 */
public class QCoinRechargeableDevice extends TargetInterface {

    private Rmb rmb;

    public QCoinRechargeableDevice(Rmb rmb) {
        this.rmb = rmb;
    }

    @Override
    public void buyGameProps() {
        System.out.println("一共购买了" + rmb.getCount() * 10 + "个道具");
    }
}
  • Main
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.itlemon.design.pattern.chapter02.adapter.example4;

/**
 * @author jiangpingping
 * @date 2018/9/10 下午9:16
 */
public class Main {

    public static void main(String[] args) {
        TargetInterface qCoinRechargeableDevice = new QCoinRechargeableDevice(new Rmb(10));
        qCoinRechargeableDevice.buyGameProps();
    }
}
使用委托(对象)的适配器UML类图

使用委托的适配器有一个特点就是Adapter拥有了Adaptee,并继承了Target抽象类,这就是三者之间的关系。

浅析适配器模式中的重要角色

适配器设计模式也是一个比较常用的设计模式之一,现对适配器设计模式中的角色进行浅析。

  • Target(对象) 该角色负责定义最终的需求,也就是使用适配器模式之后的最终效果。在本次示例中,TargetInterface就是扮演了这个Target角色。
  • Adaptee(被适配) 该角色定义的是原始的功能,它也许无法直接被利用,但是又不能随意更改,所以它就需要被适配,使得在不修改原始代码的情况下能激活Target的功能。在本次示例中,Rmb扮演了这个角色。
  • Adapter(适配) 该角色是适配器设计模式的核心角色,他负责适配AdapteeTarget,使得Adaptee来满足Target的需求。在本次示例中,QCoinRechargeableDevice扮演了这个角色。
  • Client(请求者) 该角色负责调用Target的方法来进行一系列的逻辑处理。在本次示例中,Main类扮演了这个角色。
适配器设计模式UML类图

分析完适配器设计模式的重要角色,当然也得理清适配器设计模式的UML类图。

  • 使用继承的适配器设计模式类图
  • 使用委托的适配器设计模式类图
为什么要使用适配器设计模式

我们往往有这种思想,要使用什么类的方法,直接使用不就OK了,或者稍微修改一下已有的代码不就可以使用了吗?其实这种思想是不正确的,因为在现有类的基础下,很多类的方法都经过了严格的测试,贸然地去修改他容易造成意外情况的发生,我们使用适配器设计模式,往往无需修改现有的代码,直接在现有的代码的基础上创建新的代码,这样即使出了错误,我们也能很快从我们新写的代码中找出端倪。使用适配器设计模式,也是对现有代码的一种重复利用。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/09/10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
asp.net中窗口相关操作总结(javascript)
1.打开新窗口   这个简单:Response.Write(@    language=javasJavaScript
Java架构师必看
2020/09/30
1.2K0
发布一个日期选择控件(ASPNET2.0)
The Coolest DHTML Calendar,这是一个在GPL下发布的JS日历程序,具有极高的可配置性,包括外观样式、显示格式、显示内容等等。默认程序是只提供日期选择的,需要设置几个showtime参数才能显示时间选择。下载的程序包(zip)里面已经有详细的说明文档和例子. 这个控件的aspnet 1.1版本的代码在我的donews blog上,代码可以自由修改发布. 代码和示例下载地址:demo using System; using System.Web; using System.Web.U
张善友
2018/01/19
2.1K0
jQuery Gallery Plugin在Asp.Net中使用
jQuery Gallery Plugin在Asp.Net中使用 推荐一个简单易用的Gallery插件:jQuery Gallery Plugin 下面是在Asp.Net开发中应用 示例截图: image.png -------------------------------------------------------------------------------风骚分隔线----------------------------------------------------------------
Porschev
2018/01/16
1.3K0
jQuery Gallery Plugin在Asp.Net中使用
Asp.Net无刷新分页( jquery.pagination.js)
采用Jquery无刷新分页插件jquery.pagination.js 实现无刷新分页效果 友情提示:本示例Handler中采用StringBuilder的append方法追加HTML,小数据量可以,但是大数据或是布局常变,建议返回JSON格式的数据,性能和灵活性更好! 1.插件参数列表 image.png 2.页面内容: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default
Porschev
2018/01/16
3.3K0
Asp.Net无刷新分页( jquery.pagination.js)
Asp.Net统一前后端提示信息方案
要解决的问题 减少弹框前后端代码量 增强可维护性 前后端提示资源统一 可以实现简单的多语言提示方案 增强重用性和易用性       ... 用自定义XML汇总提示信息
Porschev
2018/01/16
1.7K0
Asp.Net统一前后端提示信息方案
Asp.Net开发等级星使用(Jquery Rating)
插件参数: rater第一个参数是AJAX提交的URL rater第二个参数 maxvalue:最大星数 curvalue:默认选择多少颗星 title:鼠标放在星上的提示 enable:可设置true or false,控件是否可用 rater的第三个参数回调函数 aspx页代码: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"%> <!DOCTYPE html P
Porschev
2018/01/16
8050
Asp.Net开发等级星使用(Jquery Rating)
C#用于对用户输入数据进行校验的类
这个C#类包含了各种常用数据验证的函数,包含验证是否是数字,校验email格式,区分中英文截取字符串,区分中英文计算字符串长度,检测是否包含中文字符,判断是否包含特定字符等
用户7108768
2021/11/02
2.3K0
3,最简单的树(不显示图标)
-----后台 using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Data; using System.Text; public partial class _Default : System.Web.UI.Page {     protected voi
py3study
2020/01/07
8030
c# asp.net 实现分页(pager)功能
分页PagerHelper辅助类 using System; using System.Web; public class PagerHelper { #region 获取分页的Html代码 /// <summary> /// 获取分页的Html代码 /// 当前页码方法内部根据Request["page"]获取 /// </summary> /// <param name="pageSize">每一页数量</param> /// <param nam
纯粹是糖
2018/03/14
2.5K0
c#  asp.net 实现分页(pager)功能
C# 实现的Javascript加密算法
不打算公开的,不过转念一想,反正又没人看,就发出来吧。 这是使用C#来实现的JS加密的,被加密的JS文件,代码需要写的规范,不能少了分号(;)等js中可能会被忽略的符号。 下面是完整代码,自己看吧
徐大嘴
2019/03/21
1.1K0
asp.net中显示DataGrid控件列序号的几种方法
在aps.net中多数据绑定的控件很多,论功能来说,应该属DataGrid最为齐全,但它没有提供现成的显示记录序号的功能,不过我们可以通过它所带的一些参数来间接得到序号,下面来看看怎样得到和显示序号值计算方式如下:
Java架构师必看
2021/03/22
1.9K0
在asp.net中为Web用户控件添加属性和事件
在90年代初,Microsoft为Web程序员提供的ActiveServerPages(ASP运维
Java架构师必看
2020/10/15
3K0
Asp.Net就业课之案例实践第二次课
张哥编程
2024/12/13
660
Asp.Net就业课之案例实践第二次课
CodeSmith系列(二)——使用CodeSmith生成ASP.NET后台代码
因为表单的后台代码都差不多,所以采用了CodeSmith生成。由于表单的控制是基于XML的,所以可以根据XML自定义生成。由于没时间,就不多写了,具体模板代码见最后。 在这里,先选择变量。如下: X
雪雁-心莱科技
2018/12/27
3.2K0
分享一篇关于C#大文件上传的整个过程
1、创建一个web mvc项目,在创建一个webservice文件夹,在文件夹下创建一个SaveFileWebForm.axpx接口,
用户7053485
2020/03/12
2.9K0
ASP.NET 使用Ajax
之前在Ajax初步理解中介绍了对Ajax的初步理解,本文将介绍在ASP.NET中如何方便使用Ajax,第一种当然是使用jQuery的ajax,功能强大而且操作简单方便,第二种是使用.NET封装好的ScriptManager。
全栈程序员站长
2022/09/06
3K0
ASP.NET 使用Ajax
ASP.Net Web Page深入探讨
这篇文章经典,看过之后大受启发。值得一看!看来ASP.NET跟JSP其实是一样的,本质上没区别,ASP.NET能做到的JSP一样可以做到,反之亦然。只不过ASP.NET的框架看起来先进一点,不过JSP也在发展,似乎也出现了类似ASP.NET的框架。 一、服务器脚本基础介绍 首先,我们先复习一下Web服务器页面的基本执行方式: 1、 客户端通过在浏览器的地址栏敲入地址来发送请求到服务器端 2、 服务器接收到请求之后,发给相应的服务器端页面(也就是脚本)来执行,脚本产生客户端的响应,发送回客户端 3、 客户
用户1075292
2018/01/23
2.4K0
ASP.NET AJAX UpdatePanel 控件实现剖析
使用ASP.NET AJAX框架我们可以搭建快速响应、具有丰富的用户体验的AJAX Web应用程序,而该框架的UpdatePanel控件则提供了一种非常简单的方式来实现Web页面的局部更新,我们不需要在每次回发的时候都加载整个页面。 那这个控件是如何实现这种局部刷新的哪,透过其实现机制我们可以更清楚其优缺点,便于我们确定其使用场合。本文将重点阐述ASP.NET AJAX控件UpdatePanel的实现机制。 1. ASP.NET AJAX 简介 ASP.NET AJAX是微软在ASP.NET 2.0之上对A
葡萄城控件
2018/01/10
7.2K0
ASP.NET AJAX UpdatePanel 控件实现剖析
MSDN官方的ASP.Net异步页面的经典示例代码
示例1.演示异步获取一个网址的内容,处理后显示在OutPut这一Label上 using System; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Net; using System.IO; using System.Text; using System.Text.RegularExpressions; public partial class AsyncPag
菩提树下的杨过
2018/01/24
1.1K0
浅谈ASP.NET的Postback
说道ASP.NET的Postback,就得说Web Page的生命周期,但是Web Page的生命周期却不是三言两语就能够说得清楚的,所以在这里单纯站的编程的角度,撇开Web Page 的生命周期浅谈Postback。 我们知道,无论是ASP.NET1.x,2.0,甚至是以后的版本,ASP.NET最终Render到Client端通过浏览器浏览的都是一样:一个单纯的HTML。Client通过Submit Form的方式将填入Form的数据提交给Server进行处理。我们现在来看看ASP.NET整个Postba
蒋金楠
2018/01/16
1.2K0
浅谈ASP.NET的Postback
推荐阅读
相关推荐
asp.net中窗口相关操作总结(javascript)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验