本文转载请注明出处 —— polobymulberry-博客园html
这篇文章主要是对代理模式和委托模式进行了对比,我的认为Objective-C中的delegate大部分用法属于委托模式。全文有些抠概念,对实际开发没有任何影响。ios
前段时间看到的一篇博客iOS开发——从一道题看Delegate,和这篇博客iOS APP 架构漫谈解决的问题相似。两篇blog都写得很不错,都是为了解决两个页面之间的数据传递问题:git
A页面中有一个UILabel *labelA,B页面中有一个UITextField *textFieldB。从A页面跳转到B页面后,更改textFieldB中数据再返回到A页面,labelA显示的将是textFieldB中更改后的数据,嗯,就是这么简单的一个数据传递场景。github
解决这个问题方法不少,好比使用一个DAO(data access object)去维护labelA和textFieldB所对应的数据。页面的数据流向以下图这样:设计模式
可是这个场景不是很复杂,因此并不须要引入DAO这么重的架构。架构
有时候咱们会陷入技术的细节不可自拔,不妨静下来想想,这个问题本质在什么?ide
这个问题的难点在于页面B中textFieldB的数据变化后没法通知页面A中的labelA。若是页面B中有labelA的引用就行了,这样就能够直接在页面B的代码中操做labelA。因而我在页面B中添加了一个UILabel *labelARef,在A页面push到B页面时,将页面A的labelA赋值给labelRef便可(亲测能够进行数据传递)。函数
上述方法确实可行,不过你们确定都以为这样设计也是太粗暴了。若是数据传递的业务比较多,那么页面B中就须要引用不少页面A的属性。固然咱们能够直接引用页面A做为页面B的属性,即UIViewController *vcA。以下图所示:atom
这样设计其实没啥问题。不过咱们此次主题是代理模式,那咱们说的这个问题到底和代理模式有什么联系呢?spa
咱们先看看GoF《设计模式:可复用面向软件的基础》中对代理模式的描述:为其余对象提供一种代理以控制对这个对象的访问。咦,是否是和上面这个问题很像?为页面B提供一种代理以控制页面A的访问,能控制页面A,那就能控制页面A中的labelA。但是上面那种直接引用对象的方法也能够提供对这个对象的访问啊,为何必定要经过代理呢?咱们来看下代理模式的UML图:
注意上图中Proxy和RealSubject都实现了Subject这个接口,而且实现了相同的接口函数DoAction(),另外Proxy存有一份RealSubject的引用,即图中的delegate。通常来讲,Proxy在实现DoAction时,会调用RealSubject的DoAction,也就是利用所引用的delegate调用RealSubject的DoAction。按照我本身的理解,之因此会出现代理模式,是因为用户须要对RealSubject的DoAction功能进行扩展,又没法对RealSubject中的DoAction直接进行修改(并且也违反了封闭-开放原则),因而使用了Proxy对RealSubject的DoAction进行了扩展,而扩展的内容都是DoAction,因此又将DoAction抽象出来,作成了接口。
回到上面那个案例,咱们能够利用代理模式进行以下架构设计:
这里介绍一个小技巧,即如何辨别谁是代理 —— 直接跟Client打交道的是代理,此处Client就是ViewControllerB的textFieldB控件,因此直接打交道的就是ViewControllerB,也就是说ViewControllerB是代理。
代码以下:
// DataTransDelegate
// DataTransDelegate @protocol DataTransDelegate <NSObject> - (void)didTextFieldChanged:(UITextField *)textField; @end
// ViewControllerA
// ViewControllerA.m #import "ViewControllerA.h" #import "ViewControllerB.h" #import "DataTransDelegate.h" @interface ViewControllerA () <DataTransDelegate> @property (strong, nonatomic) UILabel *labelA; @property (strong, nonatomic) UIButton *buttonA; @end @implementation ViewControllerA - (void)viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.labelA]; [self.view addSubview:self.buttonA]; [self.buttonA addTarget:self action:@selector(pushVC) forControlEvents:UIControlEventTouchUpInside]; } - (void)pushVC { ViewControllerB *vcB = [[ViewControllerB alloc] init]; vcB.delegate = self; [self.navigationController pushViewController:vcB animated:NO]; } - (void)didTextFieldChanged:(UITextField *)textField { self.labelA.text = textField.text; } - (UILabel *)labelA { if (_labelA == nil) { _labelA = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 50)]; _labelA.text = @"显示vcB中的textField内容"; } return _labelA; } - (UIButton *)buttonA { if (_buttonA == nil) { _buttonA = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 100, 50)]; _buttonA.backgroundColor = [UIColor blueColor]; [_buttonA setTitle:@"进入vcB" forState:UIControlStateNormal]; } return _buttonA; } @end
// ViewControllerB
// ViewControllerB.h @protocol DataTransDelegate; @interface ViewControllerB : UIViewController @property (nonatomic, weak) id<DataTransDelegate> delegate; @end // ViewController.m #import "ViewControllerB.h" #import "DataTransDelegate.h" @interface ViewControllerB () <UITextFieldDelegate, DataTransDelegate> @property (strong, nonatomic) UITextField *textFieldB; @end @implementation ViewControllerB - (void)viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.textFieldB]; self.textFieldB.delegate = self; } - (void)textFieldDidEndEditing:(UITextField *)textField { [self didTextFieldChanged:textField]; } - (void)didTextFieldChanged:(UITextField *)textField { [self.delegate didTextFieldChanged:textField]; } - (UITextField *)textFieldB { if (_textFieldB == nil) { _textFieldB = [[UITextField alloc] initWithFrame:CGRectMake(100, 100, 100, 50)]; _textFieldB.text = @"输入文字"; _textFieldB.backgroundColor = [UIColor redColor]; } return _textFieldB; } @end
效果以下:
其实到目前为止并无什么异样。关键是在你们对Objective-C的protocol使用上,通常是结合delegate使用的。大多数咱们称这种模式是代理模式,可是我以为delegate更像是一种委托模式,而非真正意义上的代理,代理是proxy,而委托是delegate。另外,代理模式中代理和被代理者都须要继承并实现同一个接口Subject,而咱们使用delegate通常只须要让其中一个类继承并实现对应接口便可。
委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另外一个对象来处理。其实上面的viewControllerB包含了viewControllerA的引用这种作法就是委托模式。
好比咱们最为熟知的UITableView,就是一个典型的委托模式,它将tableView的中不变的部分封装起来,将常常变化的部分委托给用户本身处理,因此说UITableView就是一个delegator,而遵循UITableViewDelegate的那个类就是delegate,因此咱们常常会在一个UIViewController中使用相似self.tableView.delegate = self这样的表达;
你们可能会疑惑为何还须要使用UITableViewDelegate这种相似于Java中的interface?我我的理解是由于这样方便统一接口,接口统一了,方便了用户,由于只须要实现这几个接口就能够了。
因此咱们能够看到最开始提到的两篇博客其实借助了Objective-C中的protocol实现了的实际上是委托模式。
若是非要说委托模式和代理模式什么关系的话,我以为代理模式应该算是一种特殊的委托模式。