适配器模式

适配器模式被设计用于与已有代码进行交互。适配器适用于允许两个已存在的对象在一起工作,即使他们的接口不兼容。就像允许USB键盘出入PS/2端口一样,适配对象位于两个不同接口之间,对两个接口进行即时转换。适配器对象的唯一工作就是执行转换,转换可能涉及很多任务,比如将参数转换成另一种格式,重新排列参数的顺序,调用名称不同的方法或提供默认参数。

在结构上,适配器模式类似于一个简化的装饰器模式。装饰器模式通常提供它们所替代的相同接口,而适配器模式在两个不同的接口之间进行匹配。下图是适配器在UML中的表现形式:

这里,接口1期望调用一个称为make_action(some, args)的方法。我们已经有个很完美的接口2能做到我们想要的一切(并且为了避免重复,我不想重写他们),但是提供的方法被命名为diffe_action(other, args)。适配器类将会实现make_action接口并将参数匹配到已有的接口。

这里的优势在于,代码一次性的对一个接口到其他所有借口进行了全部匹配。另一种方法则使得我们每次访问这个代码时,都需要在多个不同的地方对其进行直接转换。

适配器模式例子

假设我们有以下的类,以格式'YYYY-MM-DD'接收一个字符串的日期并通过日期计算一个人的年龄:

class AgeCaculator(object):
    def __init__(self, birthday):
        self.year, self.month, self.day = (int(x) for x in birthday.split('-'))
    
    def calculate_age(self, date):
        year, month, day = (int(x) for x in data.split('-'))
        age = year - self.year
        if (month, day) < (self.month, self.day):
            age -= 1
        return age 

这是一个非常简单的类,可以一眼看出他在做什么。但是,我们必须考虑一下程序员的想法,这里使用了一个特定格式的字符串,而不是python中十分有用的内置datetime库。我们编写的大多数程序都将要与datetime对象,而不是字符串交互。

我们有多种方法来解决这种问题,我们可以重写这个类来接受datetime对象,这将可能会更加准确。但是,如果这个类是由第三方提供的,我们不知道他的内部是什么,或者我们根本就不允许去更改他们,我们就需要尝试一下别的东西。我们可以按照他原本的模样来使用这个类,每当我们要通过datetime.date对象来计算年龄时,我们可以调用datetime.date.strftime('%Y-%M-%d')函数将其转换为适当的格式。但是转换发生在很多地方,那么我们就需要在那么多地方都进行更改,而且一旦发生错误,我们还需要一个个的改回去。这是非常难以进行维护的,他违反了DRY(Do Not Repeat Yourself)原则。

或者,我们可以编写一个类适配器,运行正常的日期插入到一个普通的AgeCalculator类中:

import datetime

class DateAgeAdapter(object):
    def _str_date(self, date):
        return date.strftime("%Y-%m-%d")
    
    def __init__(self, birthday):
        birthday = self._str_date(birthday)
        self.calculator = AgeCaculator(birthday)
    
    def get_age(self, date):
        date = self._str_date(date)
        return self.calculator.calculate_age(date)

该适配器将datetime.datedatetime.time(他们对函数strftime有相同的接口)装换成我们普通的AgeCalculator可以使用的字符串。现在我们可以在新接口上使用原来的代码。我们将方法名改成了get_age以证明调用接口也可能在寻找不同的方法名,而不仅仅是一个不同类型的参数。

创建一个类作为适配器是实现一种模式的常用方式,但是,像往常一样,还有许多其他的方法可以做到这一点。继承和多重继承可以用于将功能添加到一个类中。例如,我们可以从date类中添加适配器,这样他的工作原理就与原来的AgeCaculator相同。

import datetime

class AgeableDate(datetime.date):
    def split(self, char):
        return self.year, self.month, self.day

在这里我们创建了一个继承自datetime.date的类,并添加了一个split方法,该方法接收一个参数(我们将其忽略掉),并返回一个年月日的元组。这与我们的AgeCalculator能够完美结合,因为该代码是一种特殊的格式调用strip函数,而在这种情况下,strip会返回一个年月日的元组。这个AgeCalculator只在意strip函数是否存在,并返回可接收的值;它不在意我们是否真的在传递一个字符串。但他确实有效。

bd = AgeableDate(1975, 6, 14)
bd
today = AgeableDate.today()
today
a = AgeCaculator(bd)
a.calculate_age(today)

# 输出:
AgeableDate(1975, 6, 14)
AgeableDate(2019, 4, 14)
43

但是这个使用继承替代的适配器常常会难以理解和维护。而使用适配器来替代继承,目的则会更加的明显。

我们也可以将函数当成适配器,这并不完全符合标准的适配器模式,但在通常情况下,你可以简单的传递数据到一个函数,然后将它以一个适当的形式返回作为进入进入另一个接口的入口。

参考:
《Python3 面向对象编程》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容