【故事剧情】

晚上九点半,Tony上了地铁,准备回家,正巧还有一个空位,赶紧走向前坐下。工作一天后,疲惫不堪的他正准备坐着打个盹小睡一会。这时进来两个小姑娘,一个小巧可爱,一个身姿曼妙;嬉笑地聊着天走到了Tony的前面。Tony犹豫了片刻后还是绅士般地给小女孩让了个座……

两个小姑娘道了声谢谢,便挤在一块坐下了,继续有说有笑地谈论着……

Amy:周末在商场里看到你和一个帅哥在一起。好你个Nina,脱单了也不告诉姐姐我,太不够意思了!怎么……想金屋藏”娇”啊!
Nina:不是啦,也是最近刚有的事,还没来得及告诉你呢。
Amy:那快说说呗!那小哥看着很高啊!
Nina:嗯,他1米85。
Amy:厉害了,你155他185,这就是传说中的最萌身高组合啊!
Nina:嗯,走在大街上,别人都回头看我们,弄的我挺不好了意思的~
Amy:你这是身在福中不知福啊!别人就是因为想求也求不到呢!
Nina:也有很气的时候啦,有时生气想打他,结果粉拳一出去就被他的大手包了饺子。
Amy:哈哈哈哈,还有呢!
Nina:还有一件很囧的事,我一抬头总是看到他的鼻毛,他一低头总是看到她的头发屑!
Amy:哈哈哈!笑的我肚子痛了……所以你们在一起,你一定要天天洗头,他一定要天天修鼻毛咯~
Nina:是啊!可麻烦了~
Amy:看来还是我这160的身高最棒了!衣服可以随便挑,更重要的是我男友175,穿上高跟鞋,我就可以挽着他的手肩并肩地走~
Nina:这就是所谓的身高不够鞋来凑吗?
Amy:不然怎么叫万能的高跟鞋呢……
Nina:好羡慕啊!在我这,高跟鞋也无能~
Amy:… …

正听的兴起时,地铁门开了。Tony才反应过来,到站了,该下车了。Tony赶忙往车门方向走,一不小心额头碰到了把手上,只好一手护着头往外跑。两个小姑娘相似一笑~

用程序来模拟生活

身材苗条、长像出众是每个人梦寐以求的,尤其是女孩子!但很多人却因为先天的原因并不能如意,这时就需要通过服装、化妆去弥补。所谓美女,三分靠长相七分靠打扮!比如身高不够,就可以通过穿高跟鞋让你看起来显得高一点;如果你本身就比较高,那穿不穿高跟鞋就没那么重要了。这里高跟鞋就起着一个适配的作用,能让你的形象增高四五厘米,下面我们就用代码来模拟一下高跟鞋在生活中的场景吧!

源码示例:

from abc import ABCMeta, abstractmethod
# 引入ABCMeta和abstractmethod来定义抽象类和抽象方法class IHightPerson(metaclass=ABCMeta):"""接口类,提供空实现的方法,由子类去实现"""@abstractmethoddef getName(self):"""获取姓名"""pass@abstractmethoddef getHeight(self):"""获取身高"""pass@abstractmethoddef appearance(self, person):"""外貌"""passclass HighPerson(IHightPerson):"""个高的人"""def __init__(self, name):self.__name = namedef getName(self):return self.__namedef getHeight(self):return 170def appearance(self):print(self.getName() + "身高" + str(self.getHeight()) + ",完美如你,天生的美女!")class ShortPerson:"""个矮的人"""def __init__(self, name):self.__name = namedef getName(self):return self.__namedef getRealHeight(self):return 160def getShoesHeight(self):return 6class DecoratePerson(ShortPerson, IHightPerson):"""有高跟鞋搭配的人"""def __init__(self, name):super().__init__(name)def getName(self):return super().getName()def getHeight(self):return super().getRealHeight() + super().getShoesHeight()def appearance(self):print(self.getName() + "身高" + str(self.getHeight()) + ", 在高跟鞋的适配下,你身高不输高圆圆,气质不输范冰冰!")class HeightMatch:"""身高匹配"""def __init__(self, person):self.__person = persondef matching(self, person1):"""假设标准身高差为10厘米内"""distance = abs(self.__person.getHeight() - person1.getHeight())isMatch = distance <= 10print(self.__person.getName() + "和" + person1.getName() + "是否为情侣的标准身高差:"+ ("是" if isMatch else "否") + ", 差值:" + str(distance))

测试代码:

def testPerson():lira = HighPerson("Lira")lira.appearance()demi = DecoratePerson("Demi");demi.appearance()haigerMatching = HeightMatch(HighPerson("Haiger"))haigerMatching.matching(lira)haigerMatching.matching(demi)

输出结果:

Lira身高170,完美如你,天生的美女!
Demi身高166, 在高跟鞋的适配下,你身高不输高圆圆,气质不输范冰冰!
Haiger和Lira是否为情侣的标准身高差:是, 差值:0
Haiger和Demi是否为情侣的标准身高差:是, 差值:4

从剧情中思考适配器模式

上面的例子中,高跟鞋起着一个适配的作用, 让你的形象增高5-7厘米,完全不在话下,而且效果立竿见影!使得一些女孩原本不符合接待员的真实身高,在鞋子的帮助下也能符合条件。如高跟鞋一样,使原本不匹配某种功能的对象变得匹配这种功能,这在程序中就叫做适配器模式。

适配器模式

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.

将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而无法一起工作的两个类能够在一起工作。

适配器模式的作用:
1. 接口转换,将原有的接口(或方法)转换成另一种接口;
2. 用新的接口包装一个已有的类;
3. 匹配一个老的组件到一个新的接口。

设计思想

适配器模式又叫变压器模式,也叫包装模式(Wrapper),它的核心思想是将一个对象经过包装或转换后使它符合指定的接口,使得调用方可以像使用这接口的一般对象一样使用它。这一思想,在我们生活中可谓是处处可见,比如变压器插座,能让你像使用国内电器一样使用美标(110V)电器;还有就是各种转接头,如MiniDP转HDMI转接头、HDMI转VGA线转换器、Micro USB转Type-C转接头等。

你们知道吗?“设计模式”一词最初是来源于建筑领域,而中国古建筑是世界建筑史的一大奇迹(如最具代表性的紫禁城),中国古建筑的灵魂是一种叫榫卯结构的建造理念。

榫卯(sǔn mǎo)是两个木构件上所采用的一种凹凸结合的连接方式。凸出部分叫榫(或榫头);凹进部分叫卯(或榫眼、榫槽)。它是古代中国建筑、家具及其它木制器械的主要结构方式。

榫卯结构的经典模型如下图:

榫卯是藏在木头里的灵魂!而随着时代的变化,其结构也发生着一些变化,现在很多建材生产商也在发明和生产新型的具有榫卯结构的木板。假设木板生产商有下面两块木板,木板A是榫,木板B是卯,A、B两块木板就完全吻合。他们之间的榫卯接口是一种T字形的接口。

后来,随着业务的拓展,木板厂商增加了一种新木板C。但C是L形的接口,不能与木板A对接。为了让木板C能与木板A进行对接,就需要增加一个衔接板D进行适配,而这个D就相当于适配器。如下图:

适配器模式通常用于对已有的系统拓展新功能时,尤其适用于在设计良好的系统框架下接入第三方的接口或第三方的SDK时。在系统的最初设计阶段,最好不要把适配器模式考虑进去,除非一些特殊的场景(如你的系统本身就是要去对接和适配多种类型的硬件接口)。

适配器模式的模型抽象

类图

适配器模式的类图如下:

Target是一个接口类,是提供给用户调用的接口抽象,如上面示例中的IHightPerson。Adaptee是你要进行适配的对象类,如上面的ShortPerson。Adapter是一个适配器,是对Adaptee的适配,它将Adaptee的对象转换(或说包装)成符合Target接口的对象;如上面的DecoratePerson,将ShortPerson的getRealHeight和getShoesHeight方法包装成IHightPerson的getHeight接口。

模型说明

设计要点

适配器模式中主要三个角色,在设计适配器模式时要找到并区分这些角色:
1. 目标(Target): 即你期望的目标接口,要转换成的接口。
2. 源对象(Adaptee): 即要被转换的角色,要把谁转换成目标角色。
3. 适配器(Adapter): 适配器模式的核心角色,负责把源对象转换和包装成目标对象。

优缺点

适配器模式的优点:
  1. 可以让两个没有关联的类一起运行,起着中间转换的作用。
  2. 提高了类的复用。
  3. 灵活性好,不会破坏原有的系统。
适配器模式的缺点:
  1. 如果原有系统没有设计好(如Target不是抽象类或接口,而一个实体类),适配器模式将很难实现。
  2. 过多地使用适配器,容易使代码结构混乱,如明明看到调用的是 A 接口,内部调用的却是B接口的实现。

实战应用

有一个电子书阅读器的项目(Reader),研发之初,产品经理经过各方讨论,最后告诉我们只支持TXT和Epub格式的电子书。然后你经过仔细思考、精心设计,采用了如下图1的代码架构。在这个类图中,有一个阅读器的核心类Reader,一个TXT文档的关键类TxtBook(负责TXT格式文件的解析),和一个Epub文档的关键类EpubBook(负责Epub格式文件的解析)。

图1:阅读器类图

产品上线半年后,市场响应良好,业务部门反映:有很多办公人员也在用我们的阅读器,他们希望这个阅读器能同时支持PDF格式,这样就不用在多个阅读器神之间来回切换了。这个时候我们的程序就需要增加对PDF格式的支持,而PDF并不是核心业务,我们不会单独为其开发一套PDF解析内核,而会使用一些开源的PDF库(我们称它为第三方库),如MuPDF、TCPDF等。而开源库的接口和我们的接口并不相同(如下图2),返回的内容也不是我们直接需要的,需要经过一些转换才能符合我们的要求。

图2:第三方PDF解析库的类图

这时,我们就需要对PDF的解析库MuPDF进行适配。经过上面的学习,你一定知道这时该用适配器模式了,于是我们有了如下图3的类图结构。

图3:兼容PDF的类图结构

代码实现如下:

class Page:"电子书一页的内容"def __init__(self, pageNum):self.__pageNum = pageNumdef getContent(self):return "第 " + str(self.__pageNum) + " 页的内容..."class Catalogue:"目录结构"def __init__(self, title):self.__title = titleself.__chapters = []self.setChapter("第一章")self.setChapter("第二章")def setChapter(self, title):self.__chapters.append(title)def showInfo(self):print("标题:" + self.__title)for chapter in self.__chapters:print(chapter)class IBook:"电子书文档的接口类"def parseFile(self, filePath):passdef getCatalogue(self):passdef getPageCount(self):passdef getPage(self, pageNum):passclass TxtBook(IBook):"TXT解析类"def parseFile(self, filePath):# 模拟文档的解析print(filePath + " 文件解析成功")self.__pageCount = 500return Truedef getCatalogue(self):return Catalogue("TXT电子书")def getPageCount(self):return self.__pageCountdef getPage(self, pageNum):return Page(pageNum)class EpubBook(IBook):"TXT解析类"def parseFile(self, filePath):# 模拟文档的解析print(filePath + " 文件解析成功")self.__pageCount = 800return Truedef getCatalogue(self):return Catalogue("Epub电子书")def getPageCount(self):return self.__pageCountdef getPage(self, pageNum):return Page(pageNum)class Outline:"第三方PDF解析库的目录类"passclass PdfPage:"PDF页"def __init__(self, pageNum):self.__pageNum = pageNumdef getPageNum(self):return self.__pageNumclass ThirdPdf:"第三方PDF解析库"def __init__(self):self.__pageSize = 0def open(self, filePath):print("第三方解析PDF文件:" + filePath)self.__pageSize = 1000return Truedef getOutline(self):return Outline()def pageSize(self):return self.__pageSizedef page(self, index):return PdfPage(index)class PdfAdapterBook(ThirdPdf, IBook):"TXT解析类"def parseFile(self, filePath):# 模拟文档的解析rtn = super().open(filePath)if(rtn):print(filePath + "文件解析成功")return rtndef getCatalogue(self):outline = super().getOutline()print("将Outline结构的目录转换成Catalogue结构的目录")return Catalogue("PDF电子书")def getPageCount(self):return super().pageSize()def getPage(self, pageNum):page = self.page(pageNum)print("将PdfPage的面对象转换成Page的对象")return Page(page.getPageNum())# 导入os库
import osclass Reader:"阅读器"def __init__(self, name):self.__name = nameself.__filePath = ""self.__curBook = Noneself.__curPageNum = -1def __initBook(self, filePath):self.__filePath = filePathextName = os.path.splitext(filePath)[1]if(extName.lower() == ".epub"):self.__curBook = EpubBook()elif(extName.lower() == ".txt"):self.__curBook = TxtBook()elif(extName.lower() == ".pdf"):self.__curBook = PdfAdapterBook()else:self.__curBook = Nonedef openFile(self, filePath):self.__initBook(filePath)if(self.__curBook is not None):rtn = self.__curBook.parseFile(filePath)if(rtn):self.__curPageNum = 1return rtnreturn Falsedef closeFile(self):print("关闭 " + self.__filePath + " 文件")return Truedef showCatalogue(self):catalogue = self.__curBook.getCatalogue()catalogue.showInfo()def prePage(self):return self.gotoPage(self.__curPageNum - 1)def nextPage(self):return self.gotoPage(self.__curPageNum + 1)def gotoPage(self, pageNum):if(pageNum < 1 or pageNum > self.__curBook.getPageCount()):return Noneself.__curPageNum = pageNumprint("显示第" + str(self.__curPageNum) + "页")page = self.__curBook.getPage(self.__curPageNum)page.getContent()return page

测试代码:

def testReader():reader = Reader("阅读器")if(not reader.openFile("平凡的世界.txt")):returnreader.showCatalogue()reader.gotoPage(1)reader.nextPage()reader.closeFile()print()if (not reader.openFile("平凡的世界.epub")):returnreader.showCatalogue()reader.gotoPage(5)reader.nextPage()reader.closeFile()print()if (not reader.openFile("平凡的世界.pdf")):returnreader.showCatalogue()reader.gotoPage(10)reader.nextPage()reader.closeFile()

输出结果:

平凡的世界.txt 文件解析成功
标题:TXT电子书
第一章
第二章
显示第1页
显示第2页
关闭 平凡的世界.txt 文件平凡的世界.epub 文件解析成功
标题:Epub电子书
第一章
第二章
显示第5页
显示第6页
关闭 平凡的世界.epub 文件第三方解析PDF文件:平凡的世界.pdf
平凡的世界.pdf文件解析成功
将Outline结构的目录转换成Catalogue结构的目录
标题:PDF电子书
第一章
第二章
显示第10页
将PdfPage的面对象转换成Page的对象
显示第11页
将PdfPage的面对象转换成Page的对象
关闭 平凡的世界.pdf 文件

应用场景

  1. 系统需要使用现有的类,而这些类的接口不符合现有系统的要求。
  2. 对已有的系统拓展新功能时,尤其适用于在设计良好的系统框架下增加接入第三方的接口或第三方的SDK时。


更多更有趣的文章

想获得更多更有趣的设计模式吗?一起来阅读以下系列文章吧!

程序源码

https://github.com/luoweifu/PyDesignPattern

引导篇

生活中的设计模式——启程之前,请不要错过我【试读】

基础篇

生活中的监听模式——一坑爹的热水器

生活中的适配模式——身高不够鞋来凑

生活中的状态模式——人有少、壮、老, 水之冰、液、汽

生活中的单例模式——你是我生命的唯一

生活中的职责模式——我的假条去哪了

生活中的中介模式——找房子问中介

生活中的代理模式——帮我拿一下快递

生活中的装饰模式——你想怎么穿就怎么穿

生活中的工厂模式——你要拿铁还是摩卡

生活中的迭代模式——下一个就是你了

生活中的组合模式——自己电脑组装,价格再降三折

生活中的构建模式——你想要一辆车还是一座房

生活中的克隆模式——给你一个分身术

生活中的策略模式——怎么来不重要,人到就行

生活中的命令模式——大闸蟹,走起!

生活中的备忘模式——好记性不如烂笔头

生活中的享元模式——颜料很贵必须充分利用

生活中的外观模式——学妹别慌,学长帮你

生活中的访问模式——一千个读者一千个哈姆雷特

生活中的设计模式——与经典23种设计模式的不解渊源

生活中的设计模式——那些未完待续的设计模式

进阶篇

深入解读过滤器模式——制作一杯鲜纯细腻的豆浆

深入解读对象池技术——共享让生活更便捷

深入解读回调机制——把你技能亮出来

经验篇

谈谈我对设计模式的理解

谈谈我对设计原则的思考

谈谈我对项目重构的看法



关注【SunLogging】

长按或扫码二维码,在手机端内容

生活中的适配器模式——身高不够鞋来凑相关推荐

  1. 生活中的设计模式——启程之前,请不要错过我

    两年前CSDN出一个产品叫ink,旨在提供一个高质量写作环境.那时就有写这一系列的想法了,而且也确实写了,就在ink里写了三篇文章,后来不知道因为什么原因这个产品下架了,我的三篇文章也没了,这事也就一 ...

  2. c语言冷门小知识,生活中的冷门小知识有哪些

    其实生活中充满了冷门知识,比如喝醋能够防止晕车.花生可以祛牙黄等等.下面是学习啦小编为大家整理的关于生活中的冷门小知识,希望大家喜欢! 生活中的冷门小知识 洗衣机强档比弱档节能 很多人没注意,在同样长 ...

  3. 生活中最毁身体的坏姿势(转载)

    生活中最毁身体的坏姿势(转载) 标签:   身体   生活   1小时前 1.长时间头侧一边讲电话或靠在沙发上睡着了.将话筒夹在脖子上讲电话,肌肉会过度用力收缩,颈椎容易受伤.同样,侧着身子在沙发上睡 ...

  4. 生活中最尴尬的爆笑糗事!

    2006.9.10 07:37 你常能在喜剧中看到一些搞笑的情节,让剧中人尴尬无比,却让观众大笑.其实生活中这种例子比比皆是. A:"我最尴尬的事情是和一个帅哥初次见面,早上身体不舒服,还是 ...

  5. 皮鞋就意味着只能配正装吗?在生活中其实不是

    皮鞋就意味着只能配正装吗?其实不是的,在生活中皮鞋搭配的可能性比你想象中的还要多,一双皮鞋作为穿搭的点睛之笔,搭配好才能使自己的气质提升一个档次,皮鞋有半正式和半休闲的搭配,首先我们把一身着装分为上衣 ...

  6. 原来这才是游戏上瘾的机制 如果把它用到生活中的话

    因为所有游戏的设计都是冲着"人性的弱点"来的. 1.即时反馈 你在游戏中的任何操作,都会立马视觉化.数据化地显示出来. 不要小看每次砍怪物头上飚出的数字,不要小看出招的音效,不要小 ...

  7. 被快乐×××的许晴-----平淡的生活中寻找快乐

    被快乐×××的许晴-----平淡的生活中寻找快乐其实生活不是缺少美 而是我们缺少发现美的慧眼,下面是我好朋友许晴喜欢做的事情: 她喜欢没事去麦当劳喝杯冰爽茶吃双吉或者买一杯热巧坐在临窗的位置看着窗外来 ...

  8. 生物识别技术在我们日常生活中的应用

    在数字时代,对安全性的需求比以往任何时候都高,因为我们大多数人将很大一部分生活都放在移动设备上.因此,移动电话和技术开发人员将生物识别技术集成到这些设备中,以防止盗窃并确保我们的财物安全.在本文中,我 ...

  9. 失败在大学生活中的三种功能

    --在2021级本科生开学典礼上的发言 清华大学写作与沟通教学中心主任公管学院长聘副教授:梅赐琪   尊敬的各位领导,各位同事,亲爱的2021级新生以及各位家长: §01 前言   首先请允许我代表我 ...

最新文章

  1. springboot 事务手动回滚_来,讲讲Spring事务有哪些坑?
  2. SAP Lumira 初探
  3. 双花证明已实现,BCH安全的0确认交易还远吗?
  4. N4 接口是 5G 产业价值的金线
  5. 企业实战之分布式锁方案一步步的演变历程!,Java数据库索引面试题
  6. 王爽汇编第九章学习笔记
  7. 推断单向链表中是否有环和查找环的入口
  8. Linux——CentOS建立一个最高权限的用户
  9. 【个人笔记 - 目录】OpenCV4 C++ 快速入门 30讲
  10. 【干货笔记】CS224n-2019 学习笔记 Lecture 01 Introduction and Word Vectors
  11. Maven—Windows操作系统中安装配置Maven环境
  12. C++中的cin.getline()和getline()函数的区别
  13. 利用史密斯圆图计算阻抗匹配网络的软件
  14. redhat server 5.4 64 bit 下安装 RealTek RTL8111E 网卡
  15. centos7下多播路由器安装及配置
  16. 如何使用Blender制作360度全景图和全景视频?
  17. 机器学习-KNN算法
  18. 打造建筑行业数字化新标杆: 软通动力联合华为云AI+RPA助力中铁十一局智能化升级
  19. echart整体图形大小调整
  20. 考研英语 - word-list-6

热门文章

  1. 如果做白平衡C语言算法,一种暗通道优先的快速自动白平衡算法
  2. Python网络编程之web服务器
  3. suPolyToVolume 0.2更新! (多线程+微调控制+Maya2014)
  4. 行者无疆——自行车,危险的运动
  5. 线上比赛无线充电组仲裁申请以及检查结果
  6. 2021年电工(高级)考试题及电工(高级)考试报名
  7. UVA - 524 Prime Ring Problem
  8. java图片转文字,秀出天际!
  9. 最全的路由器无线桥接WDS技术配置过程及所遇问题总结
  10. weblogic控制台配置修改