【原】談談對Objective-C中代理模式的誤解

來源:互聯網
上載者:User

標籤:

【原】談談對Objective-C中代理模式的誤解

本文轉載請註明出處 —— polobymulberry-部落格園

1. 前言

這篇文章主要是對代理模式和委託模式進行了對比,個人認為Objective-C中的delegate大部分用法屬於委託模式。全文有些摳概念,對實際開發沒有任何影響。

前段時間看到的一篇部落格iOS開發——從一道題看Delegate,和這篇部落格iOS APP 架構漫談解決的問題類似。兩篇blog都寫得很不錯,都是為瞭解決兩個頁面之間的資料傳遞問題:

A頁面中有一個UILabel *labelA,B頁面中有一個UITextField *textFieldB。從A頁面跳轉到B頁面後,更改textFieldB中資料再返回到A頁面,labelA顯示的將是textFieldB中更改後的資料,嗯,就是這麼簡單的一個資料傳遞情境。

解決這個問題方法很多,比如使用一個DAO(data access object)去維護labelA和textFieldB所對應的資料。頁面的資料流向如這樣:

但是這個情境不是很複雜,所以並不需要引入DAO這麼重的架構。

有時候我們會陷入技術的細節不可自拔,不妨靜下來想一想,這個問題本質在什嗎?

這個問題的痛點在於頁面B中textFieldB的資料變化後無法通知頁面A中的labelA。如果頁面B中有labelA的引用就好了,這樣就可以直接在頁面B的代碼中操作labelA。於是我在頁面B中添加了一個UILabel *labelARef,在A頁面push到B頁面時,將頁面A的labelA賦值給labelRef即可(親測可以進行資料傳遞)。

上述方法確實可行,不過大家肯定都覺得這樣設計也是太粗暴了。如果資料傳遞的業務比較多,那麼頁面B中就需要引用很多頁面A的屬性。當然我們可以直接引用頁面A作為頁面B的屬性,即UIViewController *vcA。如所示:

這樣設計其實沒啥問題。不過我們這次主題是代理模式,那我們說的這個問題到底和代理模式有什麼聯絡呢?

2.使用代理模式實現資料傳遞

我們先看看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

效果如下:

3.關於代理模式誤解

其實到目前為止並沒有什麼異樣。關鍵是在大家對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實現了的其實是委託模式。

如果非要說委託模式和代理模式什麼關係的話,我覺得代理模式應該算是一種特殊的委託模式。

【原】談談對Objective-C中代理模式的誤解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.