1. 什么是適配器
適配器模式(Adapter Pattern) 定義
Convert the interface of a class into another interface clients expect.Adapter lets classeswork together that couldn't otherwise because of incompatible interfaces.(將一個類的接口變換成客戶端所期待的另一種接口悠鞍,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。)
有時候也稱包裝樣式或者包裝(wrapper)模燥。將一個類的接口轉(zhuǎn)接成用戶所期待的咖祭。一個適配使得因接口不兼容而不能在一起工作的類能在一起工作,做法是將類自己的接口包裹在一個已存在的類中蔫骂。
2. 角色組成
適配器設(shè)計模型主要由三個角色組成么翰,分別是:
- 適配器角色(adapter)-- 將已有接口轉(zhuǎn)換成目標(biāo)接口
- 目標(biāo)角色(target) -- 目標(biāo)可以是具體的或抽象的類,也可以是接口
- 源角色或被適配者(adaptee) -- 已有接口
3. 適配器類型與實例
適配器類型可以分為三種:
- 類適配器模式
- 對象適配器模式
- 接口適配器模式或缺省適配器模式
接下來我們以手機充電適配器為例子辽旋,手機充電需要的電壓一般為5V浩嫌。國內(nèi)家用供電為220V,而日本則是110V补胚,針對不同地區(qū)的電壓码耐,我們需要一個適配器,這樣才能對手機進行充電糖儡。
我們先定義電源基礎(chǔ)類
// Power.h
#import <Foundation/Foundation.h>
@interface Power : NSObject
//輸出電壓
- (NSInteger)outputValue;
@end
// Power.m
#import "Power.h"
@implementation Power
- (NSInteger)outputValue {
return 0;
}
@end
定義好適配手機5V電壓的協(xié)議
// PowerPhoneNeedInterface.h
#import <Foundation/Foundation.h>
@protocol PowerPhoneNeedInterface <NSObject>
@required
- (NSInteger)outputPowerPhone;
@end
3.1 類適配器模式
類適配器模式是通過繼承被適配者來實現(xiàn)適配功能伐坏,UML關(guān)系圖如下:
代碼如下:
我們先定義一個被適配者角色(中國家用供電):
// PowerACChina.h
#import "Power.h"
@interface PowerACChina : Power
@end
// PowerACChina.m
#import "PowerACChina.h"
@implementation PowerACChina
- (NSInteger)outputValue {
return 220;
}
@end
接下來可以定義一個適配器(中國家用供電),來滿足手機充電輸出5V的要求
//PowerAdapterChina.h
#import "PowerACChina.h"
#import "PowerPhoneNeedInterface.h"
@interface PowerAdapterChina : PowerACChina<PowerPhoneNeedInterface>
@end
//PowerAdapterChina.m
#import "PowerAdapterChina.h"
@implementation PowerAdapterChina
- (NSInteger)outputPowerPhone {
return [self outputValue] / 44;
}
@end
這里握联,我們的適配器桦沉,繼承于被適配角色并且實現(xiàn)目標(biāo)角色每瞒,這樣通過實現(xiàn)目標(biāo)角色中的方法調(diào)用被適配角色中的方法進行運算,從而達到適配的效果纯露。接下來我們進行調(diào)用測試
+ (void)test1 {
PowerAdapterChina *adapter = [[PowerAdapterChina alloc] init];
NSInteger inputPower = [adapter outputPowerPhone];
NSLog(@"source output power = %ld\n adpater output power = %ld", [adapter outputValue], inputPower);
}
運行結(jié)果:
source output power = 220
adpater output power = 5
從代碼中我們可以看到剿骨,其實適配器做的主要工作就是為了讓被適配角色的API能夠滿足目標(biāo)角色的要求進行調(diào)用,適配器在中間做的是一個類似的中轉(zhuǎn)作用埠褪,并且不影響源角色和目標(biāo)角色原有的功能和邏輯浓利。
3.2 對象適配器模式
對象適配器模式適配器通過持有被適配角色實例,對接口進行適配钞速,UML圖如下:
接下來我們通過代碼實現(xiàn)下贷掖。
被適配者角色,依然是 PowerACChina, 這里不再進行重復(fù)闡述渴语。
接下來我們來創(chuàng)建一個新的適配器 PowerAdapterChina2
// PowerAdapterChina2.h
#import <Foundation/Foundation.h>
#import "PowerPhoneNeedInterface.h"
#import "PowerACChina.h"
@interface PowerAdapterChina2 : NSObject <PowerPhoneNeedInterface>
@property (nonatomic, strong) PowerACChina *power;
- (instancetype)initWithChinaPower:(PowerACChina *)power;
@end
// PowerAdapterChina2.m
#import "PowerAdapterChina2.h"
#import "PowerACChina.h"
@implementation PowerAdapterChina2
- (instancetype)initWithChinaPower:(PowerACChina *)power {
self = [super init];
if (self) {
_power = power;
}
return self;
}
- (NSInteger)outputPowerPhone {
return [self.power outputValue] / 44;
}
@end
有代碼可以看到苹威,我們需要在適配器PowerAdapterChina2通過持有被適配者調(diào)用源API達到轉(zhuǎn)換成滿足目標(biāo)API的效果, 我們寫個測試方法進行驗證
//對象適配
+ (void)test2 {
PowerACChina *power = [[PowerACChina alloc] init];
PowerAdapterChina2 *adapter = [[PowerAdapterChina2 alloc] initWithChinaPower:power];
NSInteger inputPower = [adapter outputPowerPhone];
NSLog(@"source output power = %ld\n adpater output power = %ld", [power outputValue], inputPower);
}
source output power = 220
adpater output power = 5
我們的測試時在創(chuàng)建適配器時驾凶,傳入?yún)?shù)為被適配角色實例牙甫,通過適配器,輸出目標(biāo)
3.3 接口適配器模式
接口適配器模式又成缺省適配器模式调违,適配器默認實現(xiàn)了所有目標(biāo)角色接口窟哺。并且適配器可以通過持有不同的被適配者實例,在內(nèi)部進行轉(zhuǎn)化為目標(biāo)角色API技肩,相對應(yīng)前兩種適配模式更加靈活且易于拓展且轨。
UML關(guān)系圖如下:
代碼如下:
我們再建一個被適配者角色(日本家用供電) PowerACJapan
// PowerACJapan.h
#import "Power.h"
@interface PowerACJapan : Power
@end
//PowerACJapan.h
#import "PowerACJapan.h"
@implementation PowerACJapan
- (NSInteger)outputValue {
return 110;
}
@end
定義接口適配器
// PowerAdapterAll.h
#import <Foundation/Foundation.h>
#import "Power.h"
#import "PowerPhoneNeedInterface.h"
@interface PowerAdapterAll : NSObject<PowerPhoneNeedInterface>
@property (nonatomic, strong) Power *power;
- (instancetype)initWithPower:(Power *)power;
@end
// PowerAdapterAll.m
#import "PowerAdapterAll.h"
#import "Power.h"
@implementation PowerAdapterAll
- (instancetype)initWithPower:(Power *)power {
self = [super init];
if (self) {
_power = power;
}
return self;
}
- (NSInteger)outputPowerPhone {
NSInteger outputPower = [self.power outputValue];
NSInteger sta = outputPower / 5;
if ([self.power isKindOfClass:PowerACJapan.class]) {
sta = 22;
} else if ([self.power isKindOfClass:PowerACChina.class]) {
sta = 44;
}
return outputPower / sta;
}
@end
調(diào)用測試:
//接口適配
+ (void)test3 {
PowerACChina *power = [[PowerACChina alloc] init];
PowerAdapterAll *adapter = [[PowerAdapterAll alloc] initWithPower:power];
NSInteger inputPower = [adapter outputPowerPhone];
NSLog(@"source output power = %ld\n adpater output power = %ld", [adapter.power outputValue], inputPower);
PowerACJapan *power2 = [[PowerACJapan alloc] init];
PowerAdapterAll *adapter2 = [[PowerAdapterAll alloc] initWithPower:power2];
NSInteger inputPower2 = [adapter2 outputPowerPhone];
NSLog(@"source output power = %ld\n adpater output power = %ld", [adapter2.power outputValue], inputPower2);
}
運行結(jié)果
source output power = 220
adpater output power = 5
source output power = 110
adpater output power = 5
如上代碼,在適配器角色中亩鬼,我們提供多個適配器角色能夠傳入不同被適配者角色能力(這里傳入的為Power子類)殖告,通過具體被適配者角色的實例使用抽象的電源引用阿蝶,適配器類實現(xiàn)于目標(biāo)角色并實現(xiàn)目標(biāo)角色的方法雳锋,在方法體中,我們進行邏輯處理羡洁,將輸入的電壓進行適配為5V電壓玷过,從而達到萬能適配的效果。
4. 分析
- 復(fù)用性:系統(tǒng)需要使用已經(jīng)存在的類筑煮,功能符合系統(tǒng)要求辛蚊,但這個類的接口不符合系統(tǒng)的需求,通過適配器模式解決不兼容的問題真仲,使這些功能類得到復(fù)用袋马。
- 耦合性:一定程度上的解耦
- 過多地使用適配器,增加系統(tǒng)理解難度秸应。
適用場景
我們在使用第三方的類庫虑凛,或者說第三方的API的時候碑宴,我們通過適配器轉(zhuǎn)換來滿足現(xiàn)有系統(tǒng)的使用需求。
你想使用現(xiàn)有的一些類的功能桑谍,但其接口不匹配你的要求延柠,你又不想修改原始類的情況
需要建立一個可以重復(fù)使用的類,用于一些彼此關(guān)系不大的類锣披,并易于擴展贞间,以便于面對將來會出現(xiàn)的類。
需要一個統(tǒng)一的輸出接口雹仿,但是輸入類型卻不可預(yù)知增热。