朋友們在開發中有沒有遇到過這種情況:開發中寫了一個視圖控件,雖然這個控件只是一個展示類的,並沒有什麼交互。但是在項目好幾個地方都用到了這個控件了,你在給這個視圖控件負值的時候是怎麼做的呢?
是不是這麼寫的呢?
-(void)laodData:(ItemModel*)model ;
這麼寫沒有錯,而且感覺很簡單。但是有兩點不好:
視圖根數據模型有耦合,視圖類引入了模型。
2.當你在項目裡其他的地方用到了這個視圖類,而且對應的模型不再是ItemModel了,而是一個新的模型,比如說ContentModel,這個時候你要再寫一個初始化方法嗎?類似:
-(void)laodData:(ContentModel*)model ;
這樣寫似乎也沒有錯,但是如多更多地方用了怎麼辦?
你有沒有想過,我不同的地方用到一個控件,為什麼要改寫好的視圖控件呢?有沒有別的方法能讓我寫的視圖控件獨立出來,不和數據模型產生耦合呢?這就用到了設計模式中的一個適配器模式了。
將一個類的接口轉換成客戶希望的另外一個接口。Adapter 模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。
適用場景:
1、已經存在的類的接口不符合我們的需求;
2、創建一個可以復用的類,使得該類可以與其他不相關的類或不可預見的類(即那些接口可能不一定兼容的類)協同工作;
3、在不對每一個都進行子類化以匹配它們的接口的情況下,使用一些已經存在的子類。
下面從一個例子中看下適配器的寫法:
項目中有一個視圖類,展示一個效果,前面是一個圖片,圖片的後面是一段文字,這個控件在項目裡多處使用,並且對應不同的數據模型:效果如下
把視圖控件的初始化數據改為接收一個遵守某個協議的數據
-(void)loadData:(id )data{ self.image = [data image]; self.contentStr = [data contentStr]; }
不管傳過來的數據是什麼類型,只要服從這個協議,實現協議裡的方法,視圖就能處理這個數據了
新建也個協議文件
@protocol ContentViewAdapterProtocol -(UIImage*)image; -(NSString*)contentStr;@end
類遵守上面的協議,並實現協議的方法,但是只是空實現
-(UIImage*)image{ return nil; } -(NSString*)contentStr{ return nil; }
添加初始化數據方法
- (instancetype)initWithData:(id)data;
這樣這個適配器就完成了。
使用適配器的時候分為類適配器和對象適配器,類適配器是針對每一個數據類創建一個適配器。創建的這個適配器類繼承於上面的那個根適配器類。如果要適配另一個數據類就需要再新建一個新的適配器類來適配。
新建真對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; } }
對象適配器的好處是在一個類裡處理不同的數據,類會少一些。但是如果要適配的類太多就會顯得很復雜,而對象適配器適配方法分散,類比較多。
類適配器 適配兩個類
第一個 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老師。
代碼github下載地址
iOS策略模式