(-) 提出问题
朋友们在开发中有没有遇到过这种情况:开发中写了一个视图控件,虽然这个控件只是一个展示类的,并没有什么交互。但是在项目好几个地方都用到了这个控件了,你在给这个视图控件负值的时候是怎么做的呢?
是不是这么写的呢?
-(void)laodData:(ItemModel*)model ;
这么写没有错,而且感觉很简单。但是有两点不好:
- 视图根数据模型有耦合,视图类引入了模型。
2.当你在项目里其他的地方用到了这个视图类,而且对应的模型不再是ItemModel了,而是一个新的模型,比如说ContentModel,这个时候你要再写一个初始化方法吗?类似:
-(void)laodData:(ContentModel*)model ;
这样写似乎也没有错,但是如多更多地方用了怎么办?
你有没有想过,我不同的地方用到一个控件,为什么要改写好的视图控件呢?有没有别的方法能让我写的视图控件独立出来,不和数据模型产生耦合呢?这就用到了设计模式中的一个适配器模式了。
(二 )适配器设计模式 简介
将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适用场景:
1、已经存在的类的接口不符合我们的需求;
2、创建一个可以复用的类,使得该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作;
3、在不对每一个都进行子类化以匹配它们的接口的情况下,使用一些已经存在的子类。
(三 )使用适配器模式重构代码
下面从一个例子中看下适配器的写法:
项目中有一个视图类,展示一个效果,前面是一个图片,图片的后面是一段文字,这个控件在项目里多处使用,并且对应不同的数据模型:效果如下
第1步 修改视图接收数据方法
把视图控件的初始化数据改为接收一个遵守某个协议的数据
-(void)loadData:(id <ContentViewAdapterProtocol>)data{
self.image = [data image];
self.contentStr = [data contentStr];
}
不管传过来的数据是什么类型,只要服从这个协议,实现协议里的方法,视图就能处理这个数据了
第2步 实现这个协议
新建也个协议文件
@protocol ContentViewAdapterProtocol <NSObject>
-(UIImage*)image;
-(NSString*)contentStr;
@end
第3步 创建根适配器类
类遵守上面的协议,并实现协议的方法,但是只是空实现
-(UIImage*)image{
return nil;
}
-(NSString*)contentStr{
return nil;
}
添加初始化数据方法
- (instancetype)initWithData:(id)data;
这样这个适配器就完成了。
第4步 针对不同的类创建适配器
使用适配器的时候分为类适配器和对象适配器,类适配器是针对每一个数据类创建一个适配器。创建的这个适配器类继承于上面的那个根适配器类。如果要适配另一个数据类就需要再新建一个新的适配器类来适配。
新建真对ItemModel的适配器:
#import "ItemModelAdeapter.h"
#import "ItemModel.h"
@implementation ItemModelAdeapter
- (instancetype)initWithData:(id)data{
self = [super init];
if (self) {
self.data = data;
}
return self;
}
-(UIImage*)image{
ItemModel *model = self.data;
return model.image;
}
-(NSString*)contentStr{
ItemModel *model = self.data;
return model.conntentStr;
}
对象适配器是只创建一个适配器类,继承于上面的根适配器。然后根据不同的数据类返回不同的数据:
#import "ModelAdapter.h"
#import "ItemModel.h"
#import "ContenModel.h"
//对象适配器
@implementation ModelAdapter
- (instancetype)initWithData:(id)data{
self = [super init];
if (self) {
self.data = data;
}
return self;
}
-(UIImage*)image{
if ([self.data isMemberOfClass:[ContenModel class]]) {
ContenModel *model = self.data;
return [UIImage imageNamed:model.imageName];
}else{
ItemModel *model = self.data;
return model.image;
}
}
-(NSString*)contentStr{
if ([self.data isMemberOfClass:[ContenModel class]]) {
ContenModel *model = self.data;
return model.conntentStr;
}else{
ItemModel *model = self.data;
return model.conntentStr;
}
}
对象适配器的好处是在一个类里处理不同的数据,类会少一些。但是如果要适配的类太多就会显得很复杂,而对象适配器适配方法分散,类比较多。
第5步 使用适配器
类适配器 适配两个类
第一个
ContenModel *contenModel = [[ContenModel alloc]init];
contenModel.conntentStr = @"时间:10:32:12";
contenModel.imageName = @"shijian";
ContentViewAdapter *modelAdapter = [[ContentModelAdeapter alloc]initWithData:contenModel];
ContentView *contentView = [[ContentView alloc]initWithFrame:CGRectMake(100, 100, 200, 20)];
[contentView loadData:modelAdapter];
[self.view addSubview:contentView];
第二个
ItemModel *itemModel = [[ItemModel alloc]init];
itemModel.conntentStr = @"心率:100次";
itemModel.image = [UIImage imageNamed:@"mapHeaderIcon"];
ContentViewAdapter *modelAdapter1 = [[ItemModelAdeapter alloc]initWithData:itemModel];
ContentView *contentView1 = [[ContentView alloc]initWithFrame:CGRectMake(100, 200, 200, 20)];
[contentView1 loadData:modelAdapter1];
[self.view addSubview:contentView1];
对象适配器
第一个
ItemModel *itemModel1 = [[ItemModel alloc]init];
itemModel1.conntentStr = @"心率:100次";
itemModel1.image = [UIImage imageNamed:@"mapHeaderIcon"];
ContentViewAdapter *modelAdapter2 = [[ModelAdapter alloc]initWithData:itemModel];
ContentView *contentView2 = [[ContentView alloc]initWithFrame:CGRectMake(100, 300, 200, 20)];
[contentView2 loadData:modelAdapter2];
[self.view addSubview:contentView2];
第二个
ContenModel *contenModel2 = [[ContenModel alloc]init];
contenModel2.conntentStr = @"时间:10:32:12";
contenModel2.imageName = @"shijian";
ContentViewAdapter *modelAdapter3 = [[ModelAdapter alloc]initWithData:contenModel];
ContentView *contentView3 = [[ContentView alloc]initWithFrame:CGRectMake(100, 400, 200, 20)];
[contentView3 loadData:modelAdapter3];
[self.view addSubview:contentView3];
(四)适配器模式的缺点
优点:
解耦合,让视图类不合数据类产生耦合,使视图类更加独立。 新增加数据类的时候不需要修改视图类。
缺点:
会新增加很多类,使系统更凌乱,代码可读性更弱了。
所以大家酌情使用
(五)鸣谢
这篇文章是看过 YouXianMing老师 的教程后,得到的启发并重构自己项目代码后写的总结。希望对大家有点帮助。在此感谢YouXianMing老师。