IOS 设计模式-适配器
1 适配器模式的概念
2 数据直接适配带来的困境
3 适配器模式实践
4 适配器模式的优缺点
我们大家都知道,平时我们的生活用电,不同的国家,电压是不同的,中国是220V,日本是100v,等等。那么我们使用的苹果手机充电,不管在哪个国家,同一个充电器,都可以给手机来充电,那么这个充电器可以适配不同的电压,那么这个充电器就相当于一个适配器。
1 适配器模式:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
2 数据直接适配带来的困境
a)直接赋值的弊端
b)用对象赋值的灵活性
c)如何降低数据层与视图层的耦合度
实例讲解:
1 直接赋值的弊端
我们假设在视图页面显示一个明信片,明信片上面显示 名字 一个分割线,一个日期,我们知道,不同的明信片 它的样式是不一样的,下面是一个明信片类
#import <UIKit/UIKit.h>
#define BUSINESS_FRAME CGRectMake(0, 0, 200, 130)
@interface BusinessCardView : UIView
/**
* 名字
*/
@property (nonatomic, strong) NSString *name;
/**
* 线条颜色
*/
@property (nonatomic, strong) UIColor *lineColor;
/**
* 电话号码
*/
@property (nonatomic, strong) NSString *phoneNumber;
BusinessCardView.m
#import "BusinessCardView.h"
@interface BusinessCardView ()
@property (nonatomic, strong) UILabel *nameLabel;
@property (nonatomic, strong) UIView *lineView;
@property (nonatomic, strong) UILabel *phoneNumberLabel;
@end
@implementation BusinessCardView
#pragma mark - 初始化
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)setup {
self.backgroundColor = [UIColor whiteColor];
self.layer.borderWidth = 0.5f;
self.layer.shadowOpacity = 0.5f;
self.layer.shadowOffset = CGSizeMake(5, 5);
self.layer.shadowRadius = 1.f;
self.layer.shadowColor = [UIColor grayColor].CGColor;
self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 10, 150, 25)];
self.nameLabel.font = [UIFont fontWithName:@"Avenir-Light" size:20.f];
[self addSubview:self.nameLabel];
self.lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 45, 200, 5)];
[self addSubview:self.lineView];
self.phoneNumberLabel = [[UILabel alloc] initWithFrame:CGRectMake(41, 105, 150, 20)];
self.phoneNumberLabel.textAlignment = NSTextAlignmentRight;
self.phoneNumberLabel.font = [UIFont fontWithName:@"AvenirNext-UltraLightItalic" size:16.f];
[self addSubview:self.phoneNumberLabel];
}
#pragma mark - 重写setter,getter方法
@synthesize name = _name;
@synthesize lineColor = _lineColor;
@synthesize phoneNumber = _phoneNumber;
- (void)setName:(NSString *)name {
_name = name;
_nameLabel.text = name;
}
- (NSString *)name {
return _name;
}
- (void)setLineColor:(UIColor *)lineColor {
_lineColor = lineColor;
_lineView.backgroundColor = _lineColor;
}
- (UIColor *)lineColor {
return _lineColor;
}
- (void)setPhoneNumber:(NSString *)phoneNumber {
_phoneNumber = phoneNumber;
_phoneNumberLabel.text = phoneNumber;
}
- (NSString *)phoneNumber {
return _phoneNumber;
}
那么我们将这个明信片显示在当前视图上:
ViewController.m
#import "ViewController.h"
#import "BusinessCardView.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
BusinessCardView *cardView = [[BusinessCardView alloc] initWithFrame:BUSINESS_FRAME];
cardView.center = self.view.center;
//直接赋值
cardView.name = @"JiKeXueYuan";
cardView.lineColor = [UIColor redColor];
cardView.phoneNumber = @"101 - 5687 - 000";
[self.view addSubview:cardView];
}
实际开发过程中,我们从服务器端接收的数据往往都是对象,或则列表数据,那么我们就不能直接给cardView.name = @"JiKeXueYuan"; 而可能会是cardView.name = someModel.name; 如果使用明信片的地方很多,那么我们这种直接赋值的地放都需要修改,大大增加了工作量。那么针对于这种问题,我们可以把数据封装成一个对象,这样或许能够更方便管理一些。我们新建一个model类:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface Model : NSObject
/**
* 名字
*/
@property (nonatomic, strong) NSString *name;
/**
* 线条颜色
*/
@property (nonatomic, strong) UIColor *lineColor;
/**
* 电话号码
*/
@property (nonatomic, strong) NSString *phoneNumber;
@end
Model.m
#import "Model.h"
@implementation Model
@end
BusinessCardView.m 添加一个方法
-(void)loadData:(Model *)data
{
self.name = data.name;
self.phoneNumbr = data.phoneNumber;
self.lineColor = data.lineColor;
}
ViewController.m
#import "ViewController.h"
#import "BusinessCardView.h"
#import "Model.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
BusinessCardView *cardView = [[BusinessCardView alloc] initWithFrame:BUSINESS_FRAME];
cardView.center = self.view.center;
// 以对象赋值
Model *model = [[Model alloc] init];
model.name = @"JiKeXueYuan";
model.lineColor = [UIColor redColor];
model.phoneNumber = @"101 - 5687 - 000";
[cardView loadData:model];
[self.view addSubview:cardView];
}
使用model减少了cardView 于当前要显示的视图的耦合度,但是又会出现一些灵活性的问题,cardView只能加载model类型的数据,现在假如我又有一个新的model NewModel, 那么cardView要显示newModel的数据,那么只能新加一个方法,假如有还有第三个model 第四个model,那么工作量就大多了。这是直接赋值,和对象赋值的一些弊端,那么下面我们将采用适配器模式来解决这种问题。
3 使用适配器
a)创建抽象适配器对象
b)适配器与视图层建立输出联系
c)适配器与数据层建立输入联系
适配器可以分为两种,一种是类适配器,一种是对象适配器。
1 创建抽象适配器,OC中抽象类一般使用协议来实现。
BusinessCardAdapterProtocol.h
#import <Foundation/Foundation.h>
@protocol BusinessCardAdapterProtocol <NSObject>
- (NSString *)name;
- (UIColor *)lineColor;
- (NSString *)phoneNumber;
@end
接下来我们让BusinessCardView 遵循这个协议
BusinessCardView.h
#import <UIKit/UIKit.h>
#import "BusinessCardAdapterProtocol.h"
#define BUSINESS_FRAME CGRectMake(0, 0, 200, 130)
@interface BusinessCardView : UIView
/**
* 名字
*/
@property (nonatomic, strong) NSString *name;
/**
* 线条颜色
*/
@property (nonatomic, strong) UIColor *lineColor;
/**
* 电话号码
*/
@property (nonatomic, strong) NSString *phoneNumber;
/**
* 加载数据(实现了BusinessCardAdapterProtocol协议的数据)
*
* @param data 实现了BusinessCardAdapterProtocol协议的数据
*/
- (void)loadData:(id <BusinessCardAdapterProtocol>)data;
@end
BusinessCardView.m
#import "BusinessCardView.h"
@interface BusinessCardView ()
@property (nonatomic, strong) UILabel *nameLabel;
@property (nonatomic, strong) UIView *lineView;
@property (nonatomic, strong) UILabel *phoneNumberLabel;
@end
@implementation BusinessCardView
#pragma mark - 初始化
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)setup {
self.backgroundColor = [UIColor whiteColor];
self.layer.borderWidth = 0.5f;
self.layer.shadowOpacity = 0.5f;
self.layer.shadowOffset = CGSizeMake(5, 5);
self.layer.shadowRadius = 1.f;
self.layer.shadowColor = [UIColor grayColor].CGColor;
self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 10, 150, 25)];
self.nameLabel.font = [UIFont fontWithName:@"Avenir-Light" size:20.f];
[self addSubview:self.nameLabel];
self.lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 45, 200, 5)];
[self addSubview:self.lineView];
self.phoneNumberLabel = [[UILabel alloc] initWithFrame:CGRectMake(41, 105, 150, 20)];
self.phoneNumberLabel.textAlignment = NSTextAlignmentRight;
self.phoneNumberLabel.font = [UIFont fontWithName:@"AvenirNext-UltraLightItalic" size:16.f];
[self addSubview:self.phoneNumberLabel];
}
- (void)loadData:(id <BusinessCardAdapterProtocol>)data {
self.name = [data name];
self.lineColor = [data lineColor];
self.phoneNumber = [data phoneNumber];
}
#pragma mark - 重写setter,getter方法
@synthesize name = _name;
@synthesize lineColor = _lineColor;
@synthesize phoneNumber = _phoneNumber;
- (void)setName:(NSString *)name {
_name = name;
_nameLabel.text = name;
}
- (NSString *)name {
return _name;
}
- (void)setLineColor:(UIColor *)lineColor {
_lineColor = lineColor;
_lineView.backgroundColor = _lineColor;
}
- (UIColor *)lineColor {
return _lineColor;
}
- (void)setPhoneNumber:(NSString *)phoneNumber {
_phoneNumber = phoneNumber;
_phoneNumberLabel.text = phoneNumber;
}
- (NSString *)phoneNumber {
return _phoneNumber;
}
@end
创建一个抽象的适配器对象
BusinessCardAdapter.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "BusinessCardAdapterProtocol.h"
@interface BusinessCardAdapter : NSObject <BusinessCardAdapterProtocol>
/**
* 输入对象
*/
@property (nonatomic, weak) id data;
/**
* 与输入对象建立联系
*
* @param data 输入的对象
*
* @return 实例对象
*/
- (instancetype)initWithData:(id)data;
@end
BusinessCardAdapter.m
#import "BusinessCardAdapter.h"
@implementation BusinessCardAdapter
- (instancetype)initWithData:(id)data {
self = [super init];
if (self) {
self.data = data;
}
return self;
}
- (NSString *)name {
return nil;
}
- (UIColor *)lineColor {
return nil;
}
- (NSString *)phoneNumber {
return nil;
}
@end
创建一个model的适配器
ModelAdapter.h
#import "BusinessCardAdapter.h"
/**
* 类适配器
*/
@interface ModelAdapter : BusinessCardAdapter
@end
ModelAdapter.m
#import "ModelAdapter.h"
#import "Model.h"
@implementation ModelAdapter
- (instancetype)initWithData:(id)data {
self = [super init];
if (self) {
self.data = data;
}
return self;
}
- (NSString *)name {
Model *data = self.data;
return data.name;
}
- (UIColor *)lineColor {
Model *data = self.data;
return data.lineColor;
}
- (NSString *)phoneNumber {
Model *data = self.data;
return data.phoneNumber;
}
@end
让我们再次展示这个cardView
ViewController.h
#import "ViewController.h"
#import "BusinessCardView.h"
#import "Model.h"
#import "ModelAdapter.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
BusinessCardView *cardView = [[BusinessCardView alloc] initWithFrame:BUSINESS_FRAME];
cardView.center = self.view.center;
Model *cardModel = [[Model alloc] init];
cardModel.name = @"JiKeXueYuan";
cardModel.lineColor = @"black";
cardModel.phoneNumber = @"101 - 5687 - 000";
// 与输入建立联系
BusinessCardAdapter *modelAdapter = [[ModelAdapter alloc] initWithData:model];
// 与输出建立联系
[cardView loadData:modelAdapter];
[self.view addSubview:cardView];
}
@end
这样我们整个适配器模式就已经完成了,下面假如我们又有一个新的model newModel要适配,那么我们就可以这样做
NewCardModel.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface NewCardModel : NSObject
/**
* 名字
*/
@property (nonatomic, strong) NSString *name;
/**
* 线条颜色
*/
@property (nonatomic, strong) NSString *colorHexString;
/**
* 电话号码
*/
@property (nonatomic, strong) NSString *phoneNumber;
@end
NewCardModel.m
#import "NewCardModel.h"
@implementation NewCardModel
@end
NewCardModelAdapter.h
#import "BusinessCardAdapter.h"
/**
* 类适配器
*/
@interface NewCardModelApater : BusinessCardAdapter
@end
NewCardModelAdapter.m
#import "NewCardModelApater.h"
#import "NewCardModel.h"
@implementation NewCardModelApater
- (instancetype)initWithData:(id)data {
self = [super init];
if (self) {
self.data = data;
}
return self;
}
- (NSString *)name {
NewCardModel *data = self.data;
return data.name;
}
- (UIColor *)lineColor {
NewCardModel *data = self.data;
// todo
if ([data.colorHexString isEqualToString:@"black"]) {
return [UIColor blackColor];
} else {
return [UIColor redColor];
}
}
- (NSString *)phoneNumber {
NewCardModel *data = self.data;
return data.phoneNumber;
}
@end
ViewController.m
#import "ViewController.h"
#import "BusinessCardView.h"
#import "NewCardModel.h"
#import "NewCardModelApater.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
BusinessCardView *cardView = [[BusinessCardView alloc] initWithFrame:BUSINESS_FRAME];
cardView.center = self.view.center;
NewCardModel *newCardModel = [[NewCardModel alloc] init];
newCardModel.name = @"JiKeXueYuan";
newCardModel.colorHexString = @"black";
newCardModel.phoneNumber = @"101 - 5687 - 000";
// 与输入建立联系
BusinessCardAdapter *modelAdapter = [[NewCardModelApater alloc] initWithData:model];
// 与输出建立联系
[cardView loadData:modelAdapter];
[self.view addSubview:cardView];
}
@end
这样我们就适配了一个新的model类,在此我们要介绍一下,类适配器,对象适配器。
像上面这样,我们对于每个model类,都建立了一个相对应的adaper类,那么这种方式就是类类适配器,这样也许每个model,我们都要建立也个adapter,有可能我们觉得会很麻烦,那么我们可不可以只使用一个适配器来适配所有的model呢,答案是可以的,下面我们建立一个CardAdapter 适配器来适配所有的model
CardAdapter.h
#import "BusinessCardAdapter.h"
/**
* 对象适配器
*/
@interface CardAdapter : BusinessCardAdapter
@end
CardAdapter.m
#import "CardAdapter.h"
#import "Model.h"
#import "NewCardModel.h"
@implementation CardAdapter
- (instancetype)initWithData:(id)data {
self = [super init];
if (self) {
self.data = data;
}
return self;
}
- (NSString *)name {
NSString *name = nil;
if ([self.data isMemberOfClass:[Model class]]) {
Model *model = self.data;
name = model.name;
} else if ([self.data isMemberOfClass:[NewCardModel class]]) {
NewCardModel *model = self.data;
name = model.name;
}
return name;
}
- (UIColor *)lineColor {
UIColor *lineColor = nil;
if ([self.data isMemberOfClass:[Model class]]) {
Model *model = self.data;
lineColor = model.lineColor;
} else if ([self.data isMemberOfClass:[NewCardModel class]]) {
NewCardModel *model = self.data;
if ([model.colorHexString isEqualToString:@"black"]) {
lineColor = [UIColor blackColor];
} else {
lineColor = [UIColor redColor];
}
}
return lineColor;
}
- (NSString *)phoneNumber {
NSString *phoneNumber = nil;
if ([self.data isMemberOfClass:[Model class]]) {
Model *model = self.data;
phoneNumber = model.phoneNumber;
} else if ([self.data isMemberOfClass:[NewCardModel class]]) {
NewCardModel *model = self.data;
phoneNumber = model.phoneNumber;
}
return phoneNumber;
}
@end
ViewController.m
#import "ViewController.h"
#import "BusinessCardView.h"
#import "Model.h"
#import "ModelAdapter.h"
#import "NewCardModel.h"
#import "NewCardModelApater.h"
#import "CardAdapter.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
BusinessCardView *cardView = [[BusinessCardView alloc] initWithFrame:BUSINESS_FRAME];
cardView.center = self.view.center;
// 直接赋值
// cardView.name = @"JiKeXueYuan";
// cardView.lineColor = [UIColor redColor];
// cardView.phoneNumber = @"101 - 5687 - 000";
// 以对象赋值
Model *model = [[Model alloc] init];
model.name = @"JiKeXueYuan";
model.lineColor = [UIColor redColor];
model.phoneNumber = @"101 - 5687 - 000";
NewCardModel *newCardModel = [[NewCardModel alloc] init];
newCardModel.name = @"JiKeXueYuan";
newCardModel.colorHexString = @"black";
newCardModel.phoneNumber = @"101 - 5687 - 000";
// 与输入建立联系
BusinessCardAdapter *modelAdapter = [[CardAdapter alloc] initWithData:model];
// 与输出建立联系
[cardView loadData:modelAdapter];
[self.view addSubview:cardView];
}
@end
这种适配器就叫对象适配器,其实它跟类适配器差不多,只不过,它只是在一个适配器中,判断了一下model的类型,然后处理不同的数据,实际应用中,至于使用类适配器,还是对象适配器,视情况而定,如果适配情况比较少,那么我们可以使用对象适配器,这样可以减少适配器文件的数量,如果适配情况比较多,那么我们可以使用类适配器,这样逻辑显得比较清晰一下,如果还使用对象适配器的话,就会导致对象适配器判断逻辑很多,对象庞大臃肿,今后不容易维护修改。
4 适配器模式的优缺点
优点:降低模块耦合度,可以让不修改现有模块的情况下,从而实现新的功能
缺点:对于不懂适配器模式的人来说 可读性比较差,java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为接口,不能为类,其使用有一定的局限性,不能将一个适配者类和他的子类同时适配到目标接口。
总结:实际使用中,我们什么时候使用适配器模式呢,一个简单的视图赋值,我们使用适配器模式需要写那么多代码,有时候我们觉得可能太麻烦,个人认为,如果我们在写一些代码给很多人用的时候,比如自己写的开源框架,那么我们就要考虑使用适配器等这些设计模式,良好的模块划分,高度的可扩展性,别人用起来才会方便快捷。如果写的代码不给别人用,比如工作中的代码实现,当人员就一两个人的时候,那么为了能够按时完成工作任务,那么这种简单粗暴的赋值方式也未尝不可。但是平时工作中,能使用设计模式有条件的情况下还是应该使用的,毕竟我们一直做代码的搬运工,编程是门艺术,我们要去实现她的美。