结构型设计模式之适配器模式:
一、含义
适配器模式又叫做变压器模式,也叫做包装模式,但是包装模式可不止一个(后续会有总结5个包装模式)。
其定义如下:
将一个类的接口变成客户端所期待的另一种接口,从而是原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
简单来说,适配器模式就是把一个接口或类转换成其他的接口或类
二、代码说明
1.主要有三个角色
1)目标角色
该角色定义转换为何种接口,也就是我们的期待接口。
2)源角色
把谁转换成目标角色,这个"谁"就是源角色,它是已经存在的、运行良好的类或对象,经过适配器角色的包装,它会成为一个崭新、靓丽的角色。
3)适配器角色
适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:把源角色转换为目标角色,怎么转换?一般是通过继承或是类关联的方式。
2.在用C实现过程中也是参考这种思想,以白天鹅适配成鸭子举例,具体实现如下:
1)适配器模式使用场景:
1 /***************************************************************************** 2 * Copyright (C) 2017-2018 Hanson Yu All rights reserved. 3 ------------------------------------------------------------------------------ 4 * File Module : AdapterPatternUsage.c 5 * Description : 适配器模式的使用 6 7 book@book-desktop:/work/projects/test/DesignPatterns/AdapterPattern$ gcc -o AdapterPatternUsage Duckling.c WhiteSwan.c AdapterPattern.c AdapterPatternUsage.c 8 book@book-desktop:/work/projects/test/DesignPatterns/AdapterPattern$ ./AdapterPatternUsage 9 鸭妈妈有五个孩子,其中四个是这样的: 10 叫声是嘎---嘎---嘎 11 外形是黄白相间,嘴长 12 会游泳 13 还有一只独特的小鸭子,模样是这样的:: 14 叫声是克鲁---克鲁---克鲁 15 外形是纯白色的,惹人喜爱 16 会游泳 17 能够飞行 18 19 * Created : 2017.07.26. 20 * Author : Yu Weifeng 21 * Function List : 22 * Last Modified : 23 * History : 24 ******************************************************************************/ 25 #include"stdio.h" 26 #include"malloc.h" 27 #include"stdlib.h" 28 #include"string.h" 29 #include"AdapterPattern.h" 30 31 32 33 34 /***************************************************************************** 35 -Fuction : main 36 -Description : 37 -Input : 38 -Output : 39 -Return : 40 * Modify Date Version Author Modification 41 * ----------------------------------------------- 42 * 2017/07/25 V1.0.0 Yu Weifeng Created 43 ******************************************************************************/ 44 int main(int argc,char **argv) 45 { 46 printf("鸭妈妈有五个孩子,其中四个是这样的: "); 47 T_Duck tDuckling =newDuckling; 48 tDuckling.Cry((void *)&tDuckling); 49 tDuckling.Appearance((void *)&tDuckling); 50 tDuckling.Behavior((void *)&tDuckling); 51 printf("还有一只独特的小鸭子,模样是这样的:: "); 52 T_UglyDuckling tUglyDuckling =newUglyDuckling; 53 tUglyDuckling.tDuck.Cry((void *)&tUglyDuckling); 54 tUglyDuckling.tDuck.Appearance((void *)&tUglyDuckling); 55 tUglyDuckling.tDuck.Behavior((void *)&tUglyDuckling); 56 57 return 0; 58 }
2)被调用者:
1 /***************************************************************************** 2 * Copyright (C) 2017-2018 Hanson Yu All rights reserved. 3 ------------------------------------------------------------------------------ 4 * File Module : AdapterPattern.c 5 * Description : 适配器模式 6 本文件是丑小鸭的具体实现(适配器角色(中转角色)) 7 以白天鹅适配成鸭子举例 8 9 * Created : 2017.07.26. 10 * Author : Yu Weifeng 11 * Function List : 12 * Last Modified : 13 * History : 14 ******************************************************************************/ 15 #include"stdio.h" 16 #include"malloc.h" 17 #include"stdlib.h" 18 #include"string.h" 19 #include"AdapterPattern.h" 20 21 /***************************************************************************** 22 -Fuction : UglyDucklingCry 23 -Description : 公有函数 24 -Input : 25 -Output : 26 -Return : 27 * Modify Date Version Author Modification 28 * ----------------------------------------------- 29 * 2017/07/26 V1.0.0 Yu Weifeng Created 30 ******************************************************************************/ 31 void UglyDucklingCry(void *i_ptThis) 32 { 33 T_UglyDuckling *ptUglyDuckling=(T_UglyDuckling *)i_ptThis; 34 ptUglyDuckling->tFatherSwan.Cry(); 35 } 36 37 /***************************************************************************** 38 -Fuction : UglyDucklingAppearance 39 -Description : 公有函数 40 -Input : 41 -Output : 42 -Return : 43 * Modify Date Version Author Modification 44 * ----------------------------------------------- 45 * 2017/07/26 V1.0.0 Yu Weifeng Created 46 ******************************************************************************/ 47 void UglyDucklingAppearance(void *i_ptThis) 48 { 49 T_UglyDuckling *ptUglyDuckling=(T_UglyDuckling *)i_ptThis; 50 ptUglyDuckling->tFatherSwan.Appearance(); 51 } 52 53 /***************************************************************************** 54 -Fuction : UglyDucklingBehavior 55 -Description : 公有函数 56 -Input : 57 -Output : 58 -Return : 59 * Modify Date Version Author Modification 60 * ----------------------------------------------- 61 * 2017/07/26 V1.0.0 Yu Weifeng Created 62 ******************************************************************************/ 63 void UglyDucklingBehavior(void *i_ptThis) 64 { 65 T_UglyDuckling *ptUglyDuckling=(T_UglyDuckling *)i_ptThis; 66 printf("会游泳 ");//丑小鸭不仅会游泳 67 ptUglyDuckling->tFatherSwan.Fly();//还会飞行 68 }
1 /***************************************************************************** 2 * Copyright (C) 2017-2018 Hanson Yu All rights reserved. 3 ------------------------------------------------------------------------------ 4 * File Module : AdapterPattern.h 5 * Description : 适配器模式 6 7 * Created : 2017.07.26. 8 * Author : Yu Weifeng 9 * Function List : 10 * Last Modified : 11 * History : 12 ******************************************************************************/ 13 #ifndef ADAPTER_PATTERN_H 14 #define ADAPTER_PATTERN_H 15 16 typedef struct Duck 17 {//ptThis有的实现类要用,有的不用,为统一使用void * 18 void (*Cry)(void *ptThis);//由于void *外部调用者无法确定传入什么类型(要根据具体实现类使用情况判断, 19 void (*Appearance)(void *ptThis);//一般都是直接传入本对象,而不是本对象的子类对象) 20 void (*Behavior)(void *ptThis);//如果不考虑统一性,可不使用void *,用具体类型代替,如T_UglyDuckling * 21 }T_Duck;//目标角色 22 23 typedef struct Swan 24 { 25 void (*Cry)();//叫声 26 void (*Appearance)();//外观 27 void (*Fly)(); 28 }T_Swan;//源角色 29 30 typedef struct UglyDuckling//适配器角色, 31 { 32 T_Swan tFatherSwan;;//源角色(通过继承获取源角色信息) 33 T_Duck tDuck;//目标角色(利用继承的信息实现鸭子接口,也就适配出鸭子) 34 }T_UglyDuckling;//适配器角色,把白天鹅适配成鸭子, 35 36 37 void DucklingCry(void *i_ptThis); 38 void DucklingAppearance(void *i_ptThis); 39 void DucklingBehavior(void *i_ptThis); 40 #define newDuckling {DucklingCry,DucklingAppearance,DucklingBehavior} 41 42 void WhiteSwanCry(); 43 void WhiteSwanAppearance(); 44 void WhiteSwanFly(); 45 #define newWhiteSwanFly {WhiteSwanCry,WhiteSwanAppearance,WhiteSwanFly} 46 47 void UglyDucklingCry(void *i_ptThis); 48 void UglyDucklingAppearance(void *i_ptThis); 49 void UglyDucklingBehavior(void *i_ptThis); 50 #define newUglyDuckling {newWhiteSwanFly,UglyDucklingCry,UglyDucklingAppearance,UglyDucklingBehavior} 51 52 #endif
1 /***************************************************************************** 2 * Copyright (C) 2017-2018 Hanson Yu All rights reserved. 3 ------------------------------------------------------------------------------ 4 * File Module : Duckling.c 5 * Description : 适配器模式 6 本文件是鸭子的具体实现 7 8 * Created : 2017.07.26. 9 * Author : Yu Weifeng 10 * Function List : 11 * Last Modified : 12 * History : 13 ******************************************************************************/ 14 #include"stdio.h" 15 #include"malloc.h" 16 #include"stdlib.h" 17 #include"string.h" 18 #include"AdapterPattern.h" 19 20 /***************************************************************************** 21 -Fuction : DucklingCry 22 -Description : 公有函数 23 -Input : 24 -Output : 25 -Return : 26 * Modify Date Version Author Modification 27 * ----------------------------------------------- 28 * 2017/07/26 V1.0.0 Yu Weifeng Created 29 ******************************************************************************/ 30 void DucklingCry(void *i_ptThis) 31 { 32 printf("叫声是嘎---嘎---嘎 "); 33 34 } 35 36 /***************************************************************************** 37 -Fuction : DucklingAppearance 38 -Description : 公有函数 39 -Input : 40 -Output : 41 -Return : 42 * Modify Date Version Author Modification 43 * ----------------------------------------------- 44 * 2017/07/26 V1.0.0 Yu Weifeng Created 45 ******************************************************************************/ 46 void DucklingAppearance(void *i_ptThis) 47 { 48 printf("外形是黄白相间,嘴长 "); 49 } 50 51 /***************************************************************************** 52 -Fuction : DucklingBehavior 53 -Description : 公有函数 54 -Input : 55 -Output : 56 -Return : 57 * Modify Date Version Author Modification 58 * ----------------------------------------------- 59 * 2017/07/26 V1.0.0 Yu Weifeng Created 60 ******************************************************************************/ 61 void DucklingBehavior(void *i_ptThis) 62 { 63 printf("会游泳 "); 64 }
1 /***************************************************************************** 2 * Copyright (C) 2017-2018 Hanson Yu All rights reserved. 3 ------------------------------------------------------------------------------ 4 * File Module : WhiteSwan.c 5 * Description : 适配器模式 6 本文件是天鹅的具体实现(源角色) 7 8 * Created : 2017.07.26. 9 * Author : Yu Weifeng 10 * Function List : 11 * Last Modified : 12 * History : 13 ******************************************************************************/ 14 #include"stdio.h" 15 #include"malloc.h" 16 #include"stdlib.h" 17 #include"string.h" 18 #include"AdapterPattern.h" 19 20 /***************************************************************************** 21 -Fuction : WhiteSwanCry 22 -Description : 公有函数 23 -Input : 24 -Output : 25 -Return : 26 * Modify Date Version Author Modification 27 * ----------------------------------------------- 28 * 2017/07/26 V1.0.0 Yu Weifeng Created 29 ******************************************************************************/ 30 void WhiteSwanCry() 31 { 32 printf("叫声是克鲁---克鲁---克鲁 "); 33 34 } 35 36 /***************************************************************************** 37 -Fuction : DucklingAppearance 38 -Description : 公有函数 39 -Input : 40 -Output : 41 -Return : 42 * Modify Date Version Author Modification 43 * ----------------------------------------------- 44 * 2017/07/26 V1.0.0 Yu Weifeng Created 45 ******************************************************************************/ 46 void WhiteSwanAppearance() 47 { 48 printf("外形是纯白色的,惹人喜爱 "); 49 } 50 51 /***************************************************************************** 52 -Fuction : DucklingBehavior 53 -Description : 公有函数 54 -Input : 55 -Output : 56 -Return : 57 * Modify Date Version Author Modification 58 * ----------------------------------------------- 59 * 2017/07/26 V1.0.0 Yu Weifeng Created 60 ******************************************************************************/ 61 void WhiteSwanFly() 62 { 63 printf("能够飞行 "); 64 }
3)执行结果:
book@book-desktop:/work/projects/test/DesignPatterns/AdapterPattern$ gcc -o AdapterPatternUsage Duckling.c WhiteSwan.c AdapterPattern.c AdapterPatternUsage.c
book@book-desktop:/work/projects/test/DesignPatterns/AdapterPattern$ ./AdapterPatternUsage
鸭妈妈有五个孩子,其中四个是这样的:
叫声是嘎---嘎---嘎
外形是黄白相间,嘴长
会游泳
还有一只独特的小鸭子,模样是这样的::
叫声是克鲁---克鲁---克鲁
外形是纯白色的,惹人喜爱
会游泳
能够飞行
4)详细代码:
https://github.com/fengweiyu/DesignThinking/tree/master/DesignPatterns/StructuralDesignPatterns/AdapterPattern
三、使用场景
1.有动机修改一个已经投产中的接口时,适配器模式可能是最适合的模式
比如系统扩展了,需要使用一个已有或新建的类,但这个类又不符合系统的接口,就可以使用适配器模式。
适配器模式是一个补偿模式,或者说是一个"补救"模式
四、优点
1.适配器模式可以让两个没有任何关系的类在一起运行,只要适配器这个角色能够搞定他们就行
2.增加了类的透明性
访问的是目标角色,但是具体的实现都委托给了源角色,而这些对高层模块是透明的,也是它不需要关心的。
3.提高了类的复用度
当然了,源角色在原有的系统中还是可以正常使用,而在目标角色中也可以充当新的演员。
4.灵活性非常好
突然不想要适配器,没问题,删除掉这个适配其就可以了,其他的代码都不用修改。
五、适配器模式的注意事项
适配器模式最好在详细设计阶段不要考虑它,它不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题。这个模式使用的主要场景是在扩展应用中,系统扩展了,不符合原有设计的时候才考虑通过适配器模式减少代码修改带来的风险。
六、适配器模式的扩展
对象适配器与类适配器
1.定义
对象适配器:通过对象层次的关联关系进行委托的(通过构造函数的接口类型的形参传入对象),而不是继承关系
类适配器:通过继承进行的适配
2.两者的区别:
类适配器是类间继承,对象适配器是对象的合成关系,也可以说是类的关联关系(通过构造函数的接口类型的形参传入对象),这是两者的根本区别。
3.使用场景对比
二者在实际项目中都会经常使用,由于对象适配器是通过类间的关联关系进行耦合的,因此在设计时就可以做到比较灵活,而适配器就只能通过覆写源角色的方法进行扩展,在实际项目中,对象适配器使用到的场景相对较多。
七、与其他模式的区别
1、适配器模式与装饰模式
相似的地方:
都是包装作用,都是通过委托方式实现其功能。
不同点是:
装饰模式包装的是自己的兄弟类,隶属于同一个家族(相同接口或父类),
适配器模式则修改非血缘关系类,把一个非本家族的对象伪装成本家族的对象,注意是伪装,因此它的本质还是非相同接口的对象。
具体来说:
1)意图不同
装饰模式的意图是加强对象的功能,
适配器模式关注的则是转化,它的主要意图是两个不同对象之间的转化
2)施与对象不同
装饰模式装饰的对象必须是自己的同宗,也就是相同的接口或父类,只要在具有相同的属性和行为的情况下,才能比较行为是增强还是减弱;
适配器模式则必须是两个不同的对象,因为它着重与转换,只有两个不同的对象才有转换的必要。
3)场景不同
装饰模式在任何时候都可以使用,只要是想增强类的功能,
而适配器模式则是一个补救模式,一般出现在系统成熟或已经构建完毕的项目中,作为一个紧急处理手段采用。
4)扩展性不同
装饰模式很容易扩展,
但是适配器模式就不同了,它在两个不同对象之间架起了一座沟通的桥梁,建立容易,去掉就比较困难了,需要从系统整体考虑是否能够撤销。
2、适配器模式与其他包装模式的区别:
自己不处理让其他人处理,这种类型的模式定义一个名字,叫做包装模式。包装模式包括:装饰模式、适配器模式、门面模式、代理模式、桥梁模式。
5个包装模式都是通过委托的方式对一个对象或一系列对象施行包装,有了包装,设计的系统才更加灵活、稳定,并且极具扩展性。从实现的角度来看,它们都是代理的一种具体表现形式,它们在使用场景上的区别如下:
1)代理模式主要用在不希望展示一个对象内部细节的场景中,此外,代理模式还可以用在一个对象的访问需要限制的场景中。
2)装饰模式是一种特殊的代理模式,它倡导的是在不改变接口的前提下为对象增强功能,或者动态添加额外职责。就扩展性而言,它比子类更灵活。
3)适配器模式的主要意图是接口转换,把一个对象的接口转换成系统希望的另外一个接口。这里的系统指的不仅仅是一个应用,也可能是某个环境,比如通过接口转换可以屏蔽外界接口,以免外界接口深入系统内部,从而提高系统的稳定性和可靠性
4)桥梁模式是在抽象层产生耦合,解决的是自行扩展的问题,它可以使两个有耦合关系的对象互不影响地扩展。
5)门面模式是一个粗粒度的封装,它提供一个方便访问子系统的接口,不具有任何的业务逻辑,仅仅是一个访问复杂系统的快速通道,没有它,子系统照样运行。