一、概念
1、適配器模式的動機
? 目前手機的充電電壓是5伏到幾十伏不等,但是我國標準居民用電電壓是220v,那么如何對手機進行充電呢?答案大家都知道,使用充電器,也叫電源適配器。在軟件開發(fā)中,有時也存在類似這種不兼容的情況,我們也可以像引入一個電源適配器一樣引入一個稱之為適配器的角色來協(xié)調這些存在不兼容的結構,這種設計方案即為適配器模式。
2、適配器模式的定義
? 適配器模式(Adapter Pattern):將一個接口轉換成客戶希望的另一個接口,使接口不兼容的那些類可以一起工作,其別名為包裝器(Wrapper)。適配器模式既可以作為類結構型模式,也可以作為對象結構型模式。
? 根據(jù)適配器類與適配者類的關系不同,適配器模式可分為對象適配器和類適配器兩種。在對象適配器模式中,適配器與適配者之間是關聯(lián)關系;在類適配器模式中,適配器與適配者之間是繼承(或實現(xiàn))關系。
3、適配器的3個角色
1)Target(目標抽象類):目標抽象類定義客戶所需接口,可以是一個抽象類或接口,也可以是具體類。
2)Adapter(適配器類):適配器可以調用另一個接口,作為一個轉換器,對Adaptee和Target進行適配,適配器類是適配器模式的核心,在對象適配器中,它通過繼承Target并關聯(lián)一個Adaptee對象使二者產(chǎn)生聯(lián)系。
3)Adaptee(適配者類):適配者即被適配的角色,它定義了一個已經(jīng)存在的接口,這個接口需要適配,適配者類一般是一個具體類,包含了客戶希望使用的業(yè)務方法。
4、對象適配器-結構圖
? 對象適配器使用較廣,結構圖如圖所示:

5、類適配器-結構圖
? 適配器類實現(xiàn)了抽象目標類接口Target,并繼承了適配者類,在適配器類的request()方法中調用所繼承的適配者類的specificRequest()方法,實現(xiàn)了適配。大部分情況下我們使用的是對象適配器,類適配器較少使用。

二、對象適配器示例
1)先定義一個Electrical電器類,它有個charge()充電方法,是目標抽象類;
2)然后定義一個Television類,繼承自Electrical,是普通的電器;
3)然后定義一個IphoneAdapter類蘋果電源適配器,繼承自Electrical類;
4)最后創(chuàng)建一個IphoneAdaptee類,里面包含了具體的iPhone充電方法。
具體代碼如下:
Electrical類:
@interface Electrical: NSObject
- (void)charge;
@end
@implementation Electrical
- (void)charge {
}
@end
Television類:
@interface Television : Electrical
@end
@implementation Television
- (void)charge { //在子類實現(xiàn)方法
NSLog(@"電視充電220v");
}
@end
IphoneAdapter類:
@interface IphoneAdapter : Electrical
- (instancetype)initWithAdaptee:(IphoneAdaptee *)adaptee; //初始化方法,引入Adaptee
@end
@interface IphoneAdapter ()
@property(nonatomic, strong) IphoneAdaptee *adaptee;
@end
@implementation IphoneAdapter
- (instancetype)initWithAdaptee:(IphoneAdaptee *)adaptee {
self = [super init];
if (self) {
_adaptee = adaptee;
}
return self;
}
- (void)charge {
[self.adaptee iphoneCharge]; //實現(xiàn)轉發(fā)調用效果
}
@end
IphoneAdaptee類:
@interface IphoneAdaptee : NSObject
- (void)iphoneCharge;
@end
@implementation IphoneAdaptee
- (void)iphoneCharge {
NSLog(@"蘋果充電5v");
}
@end
運行代碼:
- (void)viewDidLoad {
[super viewDidLoad];
Television *tv = [Television new];
[tv charge];
IphoneAdaptee *adaptee = [IphoneAdaptee new];
IphoneAdapter *adapter = [[IphoneAdapter alloc] initWithAdaptee:adaptee];
[adapter charge];
}
打印結果:
電視充電220v
蘋果充電5v
三、類適配器示例
1)和上面類似,先創(chuàng)建一個Electrical協(xié)議,有一個charge()方法,表示目標抽象類;
2)然后創(chuàng)建一個Television類,遵循Electrical協(xié)議;
3)然后創(chuàng)建一個IphoneAdaptee類,有iphoneCharge()方法,表示適配者;
4)最后創(chuàng)建IphoneAdapter類,繼承自IphoneAdaptee類,并遵循Electrical協(xié)議,表示適配器。
具體代碼如下:
Electrical協(xié)議:
@protocol Electrical <NSObject>
- (void)charge;
@end
Television類:
@interface Television : NSObject<Electrical>
@end
@implementation Television
- (void)charge {
NSLog(@"電視充電220v");
}
@end
IphoneAdaptee類:
@interface IphoneAdaptee : NSObject
- (void)iphoneCharge;
@end
@implementation IphoneAdaptee
- (void)iphoneCharge {
NSLog(@"蘋果充電5v");
}
@end
IphoneAdapter類:
@interface IphoneAdapter : IphoneAdaptee<Electrical>
@end
@implementation IphoneAdapter
- (void)charge {
[self iphoneCharge];
}
@end
運行代碼:
- (void)viewDidLoad {
[super viewDidLoad];
Television *tv = [Television new];
[tv charge];
IphoneAdapter *adapter = [IphoneAdapter new];
[adapter charge];
}
打印結果:
電視充電220v
蘋果充電5v
四、缺省適配器模式
? 缺省適配器模式是適配器模式的一種變體,其應用也較為廣泛。
1、缺省適配器模式的定義
? 缺省適配器模式(Default Adapter Pattern):當不需要實現(xiàn)一個接口所提供的所有方法時,可先設計一個抽象類實現(xiàn)該接口,并為接口中每個方法提供一個默認實現(xiàn)(空方法),那么該抽象類的子類可以選擇性地覆蓋父類的某些方法來實現(xiàn)需求,它適用于不想使用一個接口中的所有方法的情況,又稱為單接口適配器模式。
2、缺省適配器模式的3個角色
1)ServiceInterface(適配者接口):通常在該接口中聲明了大量的方法。
2)AbstractServiceClass(缺省適配器類):它是缺省適配器模式的核心類,使用空方法的形式實現(xiàn)了在ServiceInterface接口中聲明的方法,通常將它定義為抽象類。
3)ConcreteServiceClass(具體業(yè)務類):它是缺省適配器類的子類,根據(jù)需要有選擇性地覆蓋在適配器類中定義的方法。
3、缺省適配器模式的結構圖

4、Demo示例
1)先創(chuàng)建一個AnimalProtocol協(xié)議,里面定義了幾個動物的方法,是ServiceInterface適配者接口;
2)然后創(chuàng)建一個抽象類Animal,遵循AnimalProtocol協(xié)議,并實現(xiàn)所有的方法-空方法,是AbstractServiceClass缺省適配器類;
3)最后創(chuàng)建一個Fish類,繼承自Animal類,并實現(xiàn)了fish的方法,是ConcreteServiceClass具體業(yè)務類。
具體代碼如下:
AnimalProtocol類:
@protocol AnimalProtocol <NSObject>
- (void)eat;
- (void)run;
- (void)swim;
@end
Animal類:
@interface Animal : NSObject <AnimalProtocol>
@end
@implementation Animal
- (void)eat {}
- (void)run {}
- (void)swim {}
@end
Fish類:
@interface Fish : Animal
@end
@implementation Fish
- (void)eat {
NSLog(@"魚兒吃魚餌");
}
- (void)swim {
NSLog(@"魚兒能游泳");
}
@end
運行代碼:
- (void)viewDidLoad {
[super viewDidLoad];
Fish *fish = [Fish new];
[fish eat];
[fish swim];
}
打印結果:
魚兒吃魚餌
魚兒能游泳
五、總結
? 適配器模式將現(xiàn)有接口轉化為客戶類所期望的接口,實現(xiàn)了對現(xiàn)有類的復用,它是一種使用頻率非常高的設計模式,在軟件開發(fā)中得以廣泛應用。
1、優(yōu)點
? 對象適配器模式和類適配器模式都有的優(yōu)點:
1、將目標類和適配者類解耦,通過引入一個適配器類來重用現(xiàn)有的適配者類,無須修改原有結構。
2、增加了類的透明性和復用性,將具體的業(yè)務實現(xiàn)過程封裝在適配者類中,對于客戶端類而言是透明的,而且提高了適配者的復用性,同一個適配者類可以在多個不同的系統(tǒng)中復用。
3、靈活性和擴展性好,通過使用配置文件,可以很方便地更換適配器,也可以在不修改原有代碼的基礎上增加新的適配器類,完全符合“開閉原則”。
? 類適配器還有的優(yōu)點:
? 由于適配器類是適配者類的子類,因此可以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強。
? 對象適配器模式還有的優(yōu)點:
1、一個對象適配器可以把多個不同的適配者適配到同一個目標。
2、可以適配一個適配者的子類,由于適配器和適配者之間是關聯(lián)關系,根據(jù)“里氏代換原則”,適配者的子類也可通過該適配器進行適配。
2、缺點
? 類適配器缺點:
1、對于Java、C#等不支持多重類繼承的語言,一次最多只能適配一個適配者類,不能同時適配多個適配者。
2、適配者類不能為最終類,如在Java中不能為final類,C#中不能為sealed類。
3、 在Java、C#等語言中,類適配器模式中的目標抽象類只能為接口,不能為類,其使用有一定的局限性。
? 對象適配器缺點:
? 與類適配器模式相比,要在適配器中置換適配者類的某些方法比較麻煩。如果一定要置換掉適配者類的一個或多個方法,可以先做一個適配者類的子類,將適配者類的方法置換掉,然后再把適配者類的子類當做真正的適配者進行適配,實現(xiàn)過程較為復雜。
3、適用場景
1、系統(tǒng)需要使用一些現(xiàn)有的類,而這些類的接口(如方法名)不符合系統(tǒng)的需要,甚至沒有這些類的源代碼。
2、想創(chuàng)建一個可以重復使用的類,用于與一些彼此之間沒有太大關聯(lián)的一些類,包括一些可能在將來引進的類一起工作。
4、APP應用舉例
? Android的ListView就是通過adapter來實現(xiàn)所有的布局和事件操作的。
Demo地址:iOS-Design-Patterns