适配器模式,一个经常使用但是不知道叫这个名字的设计模式🤣🤣,开发者经常用,所以细致的学习了一遍,以下是自己的一些总结:
秉承着不给demo的文章不是好程序员的思想:demo,因为百度网盘链接经常失效的原因,把demo改成上传CSDN了,还能赚赚积分😁😁,如果你没有积分,请留言😱
问题:大家经常使用的UITableView是什么设计模式?其实就是适配器模式的非常典型的使用,所以说我们经常使用适配器.
适配器模式主要分为三个角色:
- 适配器(核心)
- 目标接口
- 被适配者
适配器还可以分为两种:类适配器 和 对象适配器 (后面🌰会讲解)
以UITableView为例,我们在一个VC中创建使用tableview,那么:
适配器->ViewController(实现协议->两个delegate)
目标接口->UI界面(抽象)->UITableView(Cell)
被适配者->数据(array,model)
一句话总结就是:我们通过适配器 把数据适配到UI上进行显示,讲原理还是这么的枯燥,所以下面举两个🍗
栗子一:把传统的美元兑换成我们的人民币:
首先目标接口:
#import <Foundation/Foundation.h>
//目标接口target
@protocol Target <NSObject>
-(float)getRMB;
@end
然后是我们的被适配者,也就是老的功能模块,美元:
#import <Foundation/Foundation.h>
//被适配对象(Adaptee:说白了就是老的功能模块,接口)
@interface Adaptee : NSObject
//美元
-(float)getUSD;
@end
#import "Adaptee.h"
@implementation Adaptee
-(float)getUSD{
return 1000;
}
@end
下面是最最主要的适配器,这里分两种
1.类适配器:
- 步骤一:遵从协议,实现接口;
- 步骤二:继承被适配器;
#import <Foundation/Foundation.h>
#import "Adaptee.h"
#import "Target.h"
/**
* 类适配器模式
* 注意:
* 第一点:类适配器模式Adapter需要继承被适配的类
* 第二点:类适配器模式Adapter需要实现目标接口
*
*/
@interface Adapter : Adaptee<Target>
@end
#import "Adapter.h"
@implementation Adapter
//这里,适配器,把美元适配成了人民币;
//这里是一个类适配器
-(float)getRMB{
return [self getUSD] * 7.0f;
}
@end
- 对象适配器:
- 步骤一:遵从协议,实现接口;
- 步骤二:要有一个被适配器的实例对象
#import <Foundation/Foundation.h>
#import "Target.h"
@class Adaptee;
/**
* 对象适配器模式
* 注意:
* 第一点:不需要继承
*
*/
@interface ObjectAdapter : NSObject<Target>
-(instancetype)init:(Adaptee*)adaptee;
@end
#import "ObjectAdapter.h"
#import "Adaptee.h"
@interface ObjectAdapter ()
@property (nonatomic,strong)Adaptee* adaptee;
@end
@implementation ObjectAdapter
- (instancetype)init:(Adaptee*)adaptee{
self = [super init];
if (self) {
_adaptee = adaptee;
}
return self;
}
-(float)getRMB{
return [_adaptee getUSD] * 7.0f;
}
@end
是的,就是这么简单,我们通过适配器,把我们被适配的美元转成了人民币;
可能这个例子不够形象,下面用我们的tableView举例:
1.创建我们的适配器
- 实例化一个被适配器对象,被适配器对象是 数据,所以这里,创建一个dataArray;
- 要遵从协议,去实现接口-->这里就是 UITableViewDelegate,UITableViewDataSource的协议方法了-->UI
#import <UIKit/UIKit.h>
/**
这里这个是适配器:--->且是一个对象适配器
1.实例化一个被适配器对象,被适配器对象是 数据,所以这里,创建一个dataArray
2.要遵从协议,去实现接口-->这里就是 UITableViewDelegate,UITableViewDataSource的协议方法了-->UI
*/
@interface BaseAdapter : NSObject<UITableViewDelegate,UITableViewDataSource>
@property (nonatomic,strong) NSMutableArray *dataArray;
@end
#import "BaseAdapter.h"
@implementation BaseAdapter
- (instancetype)init{
self = [super init];
if (self) {
_dataArray = [[NSMutableArray alloc] init];
}
return self;
}
//这里提供默认的实现,子类可以重写;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return _dataArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString * cellIdentifier = @"cellIdentifier";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:cellIdentifier];
}
return cell;
}
这里给出默认的实现,子类可以重写,可以达到很好的复用性;dataarray就是我们的被适配者,我们给数据创建一个model,把数据存储在model中
#import <Foundation/Foundation.h>
@interface UserModel : NSObject
@property (nonatomic,copy) NSString *title;
@property (nonatomic,copy) NSString *name;
-(instancetype)initWithTitle:(NSString*)title name:(NSString*)name;
@end
#import "UserModel.h"
@implementation UserModel
- (instancetype)initWithTitle:(NSString*)title name:(NSString*)name{
self = [super init];
if (self) {
_title = title;
_name = name;
}
return self;
}
@end
然后,我们去创建一个适配器的子类,这样就可以进行我们自己的定制了
#import "BaseAdapter.h"
@interface UserAdapter : BaseAdapter
@end
#import "UserAdapter.h"
#import "UserModel.h"
/*
这里就是对适配器的子类化,可以重写实现的接口,达到BaseAdapter的可复用性;
*/
@implementation UserAdapter
- (instancetype)init{
self = [super init];
if (self) {
//模拟的假数据
[self.dataArray addObject:[[UserModel alloc] initWithTitle:@"姓名" name:@"Dream"]];
[self.dataArray addObject:[[UserModel alloc] initWithTitle:@"国家" name:@"中国"]];
[self.dataArray addObject:[[UserModel alloc] initWithTitle:@"语言" name:@"中文"]];
}
return self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [super numberOfSectionsInTableView:tableView];//这里我们采用了默认的处理,比如有各种条件,也可以自己定义
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [super tableView:tableView numberOfRowsInSection:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell* cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
UserModel* model = [self.dataArray objectAtIndex:indexPath.row];
cell.textLabel.text = model.title;
cell.detailTextLabel.text = model.name;
return cell;
}
当然这里,我把UITableViewDelegate,UITableViewDataSource放在了一个适配器中,也可以分开用两个子适配器去写;
这样,我们的tableView只需要设置它的两个代理就可以了:
#import "ViewController.h"
#import "Adapter.h"
#import "UserAdapter.h"
/**
适配器 --> ViewController
目标接口--> UI界面(UITableView,UITableViewCell)
陪适配者--> 我们的数据
*/
@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>
/**
这里的tableVIew也是可以定制的,比如有特定的刷新动画,但是他不属于adpater
*/
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic) UserAdapter* adapter;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//客户端调用
// Adapter* adapter = [[Adapter alloc] init];
// float rmb = [adapter getRMB];
// NSLog(@"人名币:%f",rmb);
// [self initTableView];
[self initAdapter];
}
//原始代码结构方式
-(void)initTableView{
_tableView.delegate = self;
_tableView.dataSource = self;
}
//新的代码结构方式
-(void)initAdapter{
_adapter = [[UserAdapter alloc] init];
_tableView.delegate = _adapter;
_tableView.dataSource = _adapter;
}
//
//- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
// return 1;
//}
//
//- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)sectio{
// return 10;
//}
//
//- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// static NSString * showUserInfoCellIdentifier = @"userInfoCell";
// UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:showUserInfoCellIdentifier];
// if (cell == nil){
// cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
// reuseIdentifier:showUserInfoCellIdentifier];
// }
// cell.textLabel.text=@"姓名";
// cell.detailTextLabel.text = @"Dream";
// return cell;
//}
@end
这样,我就就很好的解决了VC代码臃肿的问题,同时,代码的复用性高,可维护性强,再也不是把tableView一股脑的写在一个VC中了;
以前刚接触iOS时,我对_tableView.delegate = _adapter;_tableView.dataSource = _adapter;
不大理解,怎么把类给到了一个代理,所以这里也给大家解释一下协议和抽象基类,懂得就略过了:
协议和抽象基类
协议,不定义任何实现,只声明方法,以确定符合协议的类的行为,因此,协议只是定义了抽象行为的接口,实现协议的类定义这些方法的实现,以执行真正的操作;
另一种定义高度抽象类型的方法是抽象基类(ABC),通过抽象基类,我们可以生成其他子类可以共享额默认行为,抽象基类和通常的类相似.只是预留了可以由子类重载的行为;
id<Delegate> myDelegate 这样的指针可以指向任何Delegate对象,这是非常强大,方便且灵活,你不用关心对象是什么类型,而只关心它实现了哪些方法
这样看起来,为什么有了抽象基类还要弄个协议出来呢?因为IOS没有多继承,但是可以遵从多个协议;
协议的本质也是一个id类型的类,我们定义一个协议使用的是:id<Delegate> myDelegate
,id<Delegate> 告诉编译器你不关心对象是什么类型,但它必须遵守Delegate协议,编译器就能保证所有赋值给id<NSObject>类型的对象都遵守NSObject协议,这就是为什么我们在给一个类设置代理时,如果不写<Delegate> 而直接使用代码 self.delegate = self;时会有警告;
后期会更新MVC MVP MVVM中 适配器的使用,下班了.....
网友评论