设计模式学习(七):适配器模式及其在AWTK中的应用

一、前言

本周参加了第三次设计模式研讨会,主题是适配器(Adapter)模式,接下来我们来看看该模式的具体内容。

二、适配器模式

Adapter模式:将一个类的接口转换成用户希望的另一个接口。

意图:使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

主要解决:主要解决在软件系统中,常常要将一些“现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的,此时可通过适配器来转换接口。

举个例子:开发某个模块时,有一个功能点实现起来很费劲,但之前有一个项目的模块实现了一样的功能点,但现在这个模块和之前那个的接口是不一样的,因此需要在中间加一个适配器( Adapter )。

简单理解:适配器即在不兼容的东西之间搭建一座桥梁,让二者能够一起工作。

三、实际例子

有一个绘图编辑器,可画 LineShape、PolygonShape 等基本图像,图形对象的抽象接口为Shape(即具体图像为子类)。

现在想新增一个绘制字符的子类 TextShape,这实现起来相当困难,但是目前项目中存在一个 TextView 类用于显示和编辑文本。

最理想的情况是复用 TextView 类,实现 TextShape 类 ,但之前设计 TextView 类的时候没有考虑到有 Shape 的存在,因此二者接口不兼容。

此时,我们可定义一个 TextShape 类,继承 Shape 类的接口和 TextView 类实现,将 TextView 实例作为 TextShape 的组成部分。

这个 TextShape 类就被称为 适配器( Adapter )。

四、结构与参与者

4.1 类适配器(多重继承)

在这里插入图片描述

类适配器是该设计模式的一种实现形式,Adapter继承自Target和Adapee类,Adapter类需要重写Target类的Request()函数,在Request()函数中做适当处理,调用Adaptee类的SepcificRequest()函数。最终,Target实际调用的是Adaptee类中的SepcificRequest()函数来完成相关操作,达到适配(兼容)的目的。

4.2 对象适配器(依赖对象组合)

在这里插入图片描述

对象适配器是另一种实现形式,Adapter继承自Target类,且其中有Adaptee类型的成员变量, Adaptee类重写Request()函数时,在Request()函数中使用该成员变量调用的Adaptee类中的SepcificRequest()函数,达到适配(兼容)的目的。

4.3 参与者

  • Target:定义Client使用的与特定领域相关的接口(Shape);
  • Client:与符合Target接口的对象协同(实际上就是Shape的用户);
  • Adaptee:一个已经存在的接口,这个接口需要适配(TextView);
  • Adapter:对Adaptee的接口与Target接口进行适配(TextShape);

4.4 两种适配器实现的比较

类适配器的特点

  • Adapter类直接继承Adaptee类,可以在Adapter类中对Adaptee类的方法进行重定义;
  • 在Adaptee类新增抽象方法,Adapter类也要改动,代码耦合度高;
  • 如果Adaptee类有其他子类, Adapter类中无法调用Adaptee子类的方法。

对象适配器的特点

  • 有时会发现,构造一个Adaptee 类对象比较困难。;
  • 在Adaptee类新增抽象方法, Adapter类无需修改,也能正常使用;
  • 可以使用多态的方式在Adapter类中调用Adaptee子类的方法。

总结:对象适配器耦合度比较低,能使用对象组合的方式,就尽量使用,避免多重继承,但如果构造 Adaptee 对象非常困难,那么可以考虑用类适配器。

五、适配器模式在AWTK中的应用

nanovg是一个非常小巧的矢量绘图函数库。相比cairo和skia的数十万行代码,nanovg不足5000行的C语言代码,称为nano也是名副其实了。

nanovg的设计、接口和代码质量都堪称典范,唯一美中不足的就是性能不太理想。由于nanovg本身只有 OpenGL 的实现,在Android的低端机型和大屏幕的机型上,一个简单的界面每秒只能画十几帧。

因此,AWTK 使用 nanovg 矢量绘图功能的前端,虽然其后端实现只有OpenGL,但 AWTK 补充了 AGG 、AGGE、BGFX 的后端实现。这其中便采用了适配器模式,比如兼容 nanovg 和 AGGE 接口的适配器 nanovg_agge 。

AWTK是 ZLG 开发的开源 GUI 引擎,官网地址:https://www.zlg.cn/index/pub/awtk.html

nanovg仓库:https://github.com/zlgopen/nanovg

以 AGGE 为例,结合本文 4.2 中的类图来看,nanovg 即为 Target,AGGE 即为 Adaptee,nanovg_agge 即为适配器Adapter,其中适配器 nanovg_agge 的部分代码如下,感兴趣的朋友可以查看 AWTK 源码。

//awtk\3rd\nanovg\agge\nanovg_agge.cpp
void nvgReinitAgge(NVGcontext* ctx, uint32_t w, uint32_t h, uint32_t stride, enum NVGtexture format,
                   uint8_t* data) {
  NVGparams* params = nvgGetParams(ctx);
  AGGENVGcontext* agge = (AGGENVGcontext*)(params->userPtr);

  nvgInitAGGE(agge, params, w, h, stride, format, data);
}

NVGcontext* nvgCreateAGGE(uint32_t w, uint32_t h, uint32_t stride, enum NVGtexture format, uint8_t* data) {
  NVGparams params;
  NVGcontext* ctx = NULL;
  AGGENVGcontext* agge = new AGGENVGcontext();

  if (agge == NULL) goto error;

  memset(&params, 0, sizeof(params));
  params.setLineJoin = aggenvg__setLineJoin;
  params.setLineCap = aggenvg__setLineCap;
  params.renderCreate = aggenvg__renderCreate;
  params.findTexture = aggenvg__renderFindTexture;
  params.renderCreateTexture = aggenvg__renderCreateTexture;
  params.renderDeleteTexture = aggenvg__renderDeleteTexture;
  params.renderUpdateTexture = aggenvg__renderUpdateTexture;
  params.renderGetTextureSize = aggenvg__renderGetTextureSize;
  params.renderViewport = aggenvg__renderViewport;
  params.renderCancel = aggenvg__renderCancel;
  params.renderFlush = aggenvg__renderFlush;
  params.renderDelete = aggenvg__renderDelete;
  params.clearCache = aggenvg__clearCache;
  params.userPtr = agge;
  params.edgeAntiAlias = 1;

  nvgInitAGGE(agge, &params, w, h, stride, format, data);

  ctx = nvgCreateInternal(&params);
  if (ctx == NULL) goto error;

  return ctx;

error:
  if (ctx != NULL) nvgDeleteInternal(ctx);
  return NULL;
}

void nvgDeleteAGGE(NVGcontext* ctx) {
  nvgDeleteInternal(ctx);
}

需要注意的是,观察 nanovg_agge 的构造函数 nvgCreateAGGE(),其中构建了一个 AGGE 的对象 AGGENVGcontext* agge ,通过该对象来调用 AGGE 接口中的具体实现,因此 nanovg_agge 是对象适配器。

六、总结

优点

  • 可以让两个没有关联的类一起运行,协同工作。
  • 提高了类的复用;
  • 增加了类的透明度;
  • 灵活性好;

缺点:过多使用适配器,会让系统非常凌乱,比如看到的是A接口,实际上内部被适配成了B接口,这种情况多了,无异于异常灾难,因此如果不是必要情况,尽量不要使用适配器,而是直接对系统进行重构。

应用场景:使用一个已经存在的类,如果它的接口和你实际要求不一致时,可以考虑使用适配器模式,需要注意的是最好在调用者和功能提供者双方都不太容易修改的时候再使用适配器模式,而不是一有不同就使用它。

注意事项:适配器不是在详细设计时添加的,而是解决正在服役项目的问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AWTK开发手册-AWTK开发实践指南-文手册.pdf AWTK = Toolkit AnyWhere 随着手机、智能手表等便携式设备的普及,用户对 GUI 的要求越来越高,嵌入式系统对高性能、高可靠性、低功耗、美观炫酷的 GUI 的需求也越来越迫切,ZLG开源 GUI 引擎 AWTK 应运而生。AWTK 全称为 Toolkit AnyWhere,是 ZLG 倾心打造的一套基于 C 语言开发的 GUI 框架。旨在为用户提供一个功能强大、高效可靠、简单易用、可轻松做出炫酷效果的 GUI 引擎,并支持跨平台同步开发,一次编程,终生使用。 最终目标: 支持开发嵌入式软件。 支持开发Linux应用程序。 支持开发MacOS应用程序。 支持开发Windows应用程序。 支持开发Android应用程序。 支持开发iOS应用程序。 支持开发2D游戏。 其主要特色有: 小巧。在精简配置下,不依赖第三方软件包,仅需要32K RAM + 256K FLASH即可开发一些简单的图形应用程序。 高效。采用脏矩形裁剪算法,每次只绘制和更新变化的部分,极大提高运行效率和能源利用率。 稳定。通过良好的架构设计和编程风格、单元测试、动态(valgrind)检查和Code Review保证其运行的稳定性。 丰富的GUI组件。提供窗口、对话框和各种常用的组件(用户可以配置自己需要的组件,降低对运行环境的要求)。 支持多种字体格式。内置位图字体(并提供转换工具),也可以使用stb_truetype或freetype加载ttf字体。 支持多种图片格式。内置位图图片(并提供转换工具),也可以使用stb_image加载png/jpg等格式的图片。 紧凑的二进制界面描述格式。可以手工编辑的XML格式的界面描述文件,也可以使用Qt Designer设计界面,然后转换成紧凑的二进制界面描述格式,提高运行效率,减小内存开销。 支持主题并采用紧凑的二进制格式。开发时使用XML格式描述主题,然后转换成紧凑的二进制格式,提高运行效率,减小内存开销。 支持裸系统,无需OS和文件系统。字体、图片、主题和界面描述数据都编译到代码,以常量数据的形式存放,运行时无需加载到内存。 内置nanovg实现高质量的矢量动画,并支持SVG矢量图。 支持窗口动画、控件动画、滑动动画和高清LCD等现代GUI常见特性。 支持国际化(Unicode、字符串翻译和输入法等)。 可移植。支持移植到各种RTOS和嵌入式Linux系统,并通过SDL在各种流行的PC/手机系统上运行。 脚本化。从API注释提取API的描述信息,通过这些信息可以自动生成各种脚本的绑定代码。 支持硬件2D加速(目前支持STM32的DMA2D和NXP的PXP)和GPU加速(OpenGL/OpenGLES/DirectX/Metal),充分挖掘硬件潜能。 丰富的文档和示例代码。 采用LGPL协议开源发布,在商业软件使用时无需付费。 目前核心功能已经完成,内部开始在实际项目使用了,欢迎有兴趣的朋友评估和尝试,期待您的反馈。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值