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)。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类能在一起工作,做法是将类自己的接口包裹在一个已存在的类中。
2. 角色组成
适配器设计模型主要由三个角色组成,分别是:
- 适配器角色(adapter)-- 将已有接口转换成目标接口
- 目标角色(target) -- 目标可以是具体的或抽象的类,也可以是接口
- 源角色或被适配者(adaptee) -- 已有接口
3. 适配器类型与实例
适配器类型可以分为三种:
- 类适配器模式
- 对象适配器模式
- 接口适配器模式或缺省适配器模式
接下来我们以手机充电适配器为例子,手机充电需要的电压一般为5V。国内家用供电为220V,而日本则是110V,针对不同地区的电压,我们需要一个适配器,这样才能对手机进行充电。
我们先定义电源基础类
// 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电压的协议
// PowerPhoneNeedInterface.h
#import <Foundation/Foundation.h>
@protocol PowerPhoneNeedInterface <NSObject>
@required
- (NSInteger)outputPowerPhone;
@end
3.1 类适配器模式
类适配器模式是通过继承被适配者来实现适配功能,UML关系图如下:
代码如下:
我们先定义一个被适配者角色(中国家用供电):
// 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
这里,我们的适配器,继承于被适配角色并且实现目标角色,这样通过实现目标角色中的方法调用被适配角色中的方法进行运算,从而达到适配的效果。接下来我们进行调用测试
+ (void)test1 {
PowerAdapterChina *adapter = [[PowerAdapterChina alloc] init];
NSInteger inputPower = [adapter outputPowerPhone];
NSLog(@"source output power = %ld\n adpater output power = %ld", [adapter outputValue], inputPower);
}
运行结果:
source output power = 220
adpater output power = 5
从代码中我们可以看到,其实适配器做的主要工作就是为了让被适配角色的API能够满足目标角色的要求进行调用,适配器在中间做的是一个类似的中转作用,并且不影响源角色和目标角色原有的功能和逻辑。
3.2 对象适配器模式
对象适配器模式适配器通过持有被适配角色实例,对接口进行适配,UML图如下:
接下来我们通过代码实现下。
被适配者角色,依然是 PowerACChina, 这里不再进行重复阐述。
接下来我们来创建一个新的适配器 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通过持有被适配者调用源API达到转换成满足目标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
我们的测试时在创建适配器时,传入参数为被适配角色实例,通过适配器,输出目标
3.3 接口适配器模式
接口适配器模式又成缺省适配器模式,适配器默认实现了所有目标角色接口。并且适配器可以通过持有不同的被适配者实例,在内部进行转化为目标角色API,相对应前两种适配模式更加灵活且易于拓展。
UML关系图如下:
代码如下:
我们再建一个被适配者角色(日本家用供电) 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
调用测试:
//接口适配
+ (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);
}
运行结果
source output power = 220
adpater output power = 5
source output power = 110
adpater output power = 5
如上代码,在适配器角色中,我们提供多个适配器角色能够传入不同被适配者角色能力(这里传入的为Power子类),通过具体被适配者角色的实例使用抽象的电源引用,适配器类实现于目标角色并实现目标角色的方法,在方法体中,我们进行逻辑处理,将输入的电压进行适配为5V电压,从而达到万能适配的效果。
4. 分析
- 复用性:系统需要使用已经存在的类,功能符合系统要求,但这个类的接口不符合系统的需求,通过适配器模式解决不兼容的问题,使这些功能类得到复用。
- 耦合性:一定程度上的解耦
- 过多地使用适配器,增加系统理解难度。
适用场景
我们在使用第三方的类库,或者说第三方的API的时候,我们通过适配器转换来满足现有系统的使用需求。
你想使用现有的一些类的功能,但其接口不匹配你的要求,你又不想修改原始类的情况
需要建立一个可以重复使用的类,用于一些彼此关系不大的类,并易于扩展,以便于面对将来会出现的类。
需要一个统一的输出接口,但是输入类型却不可预知。