Modele de design Delegația

Modelul de delegare se numără printre cele mai comune modele în dezvoltarea iOS și OS X. Este un model simplu, care este puternic folosit de cadrele Apple și chiar și cea mai simplă aplicație iOS se ocupă de delegarea de a-și face munca. Să începem prin examinarea definiției delegației.

1. Ce este Delegația?

Definiție

Definirea modelului de delegare este scurtă și simplă. Acesta este modul în care Apple definește modelul.

Un delegat este un obiect care acționează în numele sau în coordonare cu un alt obiect atunci când acel obiect întâlnește un eveniment într-un program.

Să lăsăm asta jos. Modelul de delegare implică două obiecte, delegatul și obiectul delegare. UITableView clasa, de exemplu, definește a delega proprietate la care deleagă evenimente. Proprietatea delegaților trebuie să respecte cerințele UITableViewDelegate protocol, care este definit în fișierul antet al UITableView clasă.

În acest exemplu, instanța de vizualizare a tabelului este obiectul delegare. Delegatul este, de obicei, un controler de vizualizare, dar poate fi orice obiect care se conformează UITableViewDelegate protocol. Dacă nu sunteți familiarizați cu protocoalele, o clasă se conformează unui protocol dacă implementează metodele necesare ale protocolului. O să vedem un exemplu mai târziu.

Când utilizatorul fixează un rând în vizualizarea de tabelă, vizualizarea tabelului îi notifică delegatul prin trimiterea unui mesaj tableView (_: didSelectRowAtIndexPath :). Primul argument al acestei metode este afișarea în tabel a mesajului. Al doilea argument este calea index a rândului pe care utilizatorul a dat-o.

Vizualizarea tabelului îi notifică doar delegatul despre acest eveniment. Depinde de delegat să decidă ce trebuie să se întâmple atunci când a avut loc un astfel de eveniment. Această separare a responsabilităților, după cum veți învăța într-un moment, este unul dintre beneficiile cheie ale modelului de delegare.

avantaje

reutilizabilitate

Delegația are mai multe avantaje, prima fiind reutilizarea. Deoarece vizualizarea tabelului deleagă interacțiunea utilizatorului cu delegatul său, vizualizarea de tabel nu trebuie să știe ce trebuie să se întâmple atunci când unul dintre rândurile sale este selectat.

Altfel, vizualizarea tabelului poate rămâne ignorantă în detaliile implementării modului în care interacțiunea cu utilizatorul este gestionată de aplicație. Această responsabilitate este delegată delegatului, de exemplu un controler de vizualizare.

Beneficiul direct este că UITableView clasa poate fi utilizată ca în cele mai multe situații. De cele mai multe ori, nu este nevoie de subclase UITableView pentru a vă adapta la nevoile aplicației dvs..

Cuplaj slab

Un alt avantaj important al delegării este cuplajul liber. În articolul meu despre singletoni, subliniez că cuplarea strânsă ar trebui evitată cât mai mult posibil. Delegația este un model de design care promovează în mod activ cuplarea liberă. Ce vreau să spun prin asta?

UITableView clasa este cuplată cu delegatul său pentru a-și desfășura activitatea. Dacă niciun delegat nu este asociat cu vizualizarea de tabelă, vizualizarea tabelului nu poate gestiona sau nu răspunde la interacțiunea utilizatorului. Aceasta înseamnă că trebuie să existe un anumit nivel de cuplare. Vederea tabelului și delegatul său sunt totuși cuplate, pentru că fiecare clasă care implementează UITableViewDelegate protocolul poate acționa în calitate de delegat al mesei de vedere. Rezultatul este un graf obiect flexibil și slab cuplat.

Separarea responsabilităților

Un avantaj mai puțin cunoscut al delegării este separarea responsabilităților. Ori de câte ori creați un grafic de obiecte, este important să știți care obiecte sunt responsabile pentru ce sarcini. Modelul de delegare face acest lucru foarte clar.

În cazul UITableView clasa, delegatul vizualizării de tabel este responsabil pentru manipularea interacțiunii utilizatorilor. Vizualizarea tabelului în sine este responsabilă pentru detectarea interacțiunii utilizatorilor. Aceasta este o separare clară a responsabilităților. O astfel de separare face munca ta ca dezvoltator mult mai ușoară și mai clară.

2. Exemplu

Există câteva arome ale modelului de delegare. Să continuăm explorând în continuare UITableViewDelegate protocol.

Delegație

 UITableViewDelegate protocolul trebuie să fie implementat de către delegatul din tabelul de vedere al mesei. Vizualizarea tabelului îi notifică delegatul prin intermediul UITableViewDelegate protocol privind interacțiunea cu utilizatorul, dar utilizează, de asemenea, delegatul pentru aspectul acestuia.

O diferență importantă între Swift și Obiectiv-C este posibilitatea de a marca metodele protocolului ca opționale. În Obiectiv-C, metodele unui protocol sunt necesare în mod implicit. Metodele UITableViewDelegate Protocolul, cu toate acestea, este opțional. Cu alte cuvinte, este posibil ca o clasă să se conformeze UITableViewDelegate fără a aplica niciuna dintre metodele protocolului.

Cu toate acestea, în Swift este necesară o clasă conformă cu un anumit protocol pentru a implementa fiecare metodă definită de protocol. Acest lucru este mult mai sigur, deoarece obiectul de delegare nu trebuie să verifice dacă delegatul implementează o metodă de protocol. Această diferență subtilă, dar importantă este ilustrată mai târziu în acest tutorial când implementăm modelul de delegare.

Sursă de date

Există un alt model care este strâns legat de modelul de delegare sursa de date. UITableViewDataSource protocol este un exemplu al acestui model. UITableView clasa expune a sursă de date proprietate care este de tip UITableViewDataSource (id în obiectivul C). Aceasta înseamnă că sursa de date a tabelului poate fi orice obiect care implementează UITableViewDataSource protocol.

Obiectul sursă de date este responsabil pentru gestionarea sursei de date a obiectului din care este sursa de date. Este important să rețineți că obiectul sursă de date este responsabil pentru păstrarea unei trimiteri la elementele pe care le expune la obiectul țintă, cum ar fi o vizualizare de tabelă sau o colecție.

O vizualizare tabelă, de exemplu, întreabă sursa de date pentru datele pe care trebuie să le afișeze. Vizualizarea tabelului nu este responsabilă pentru menținerea apăsării obiectelor de date de care are nevoie pentru afișare. Acest rol este predat obiectului sursă de date.

Modelul sursei de date se potrivește frumos în Model-View-Controller sau MVC model. De ce este asta? O vizualizare de tabelă, de exemplu, face parte din stratul de vizualizare. Nu știe și nu trebuie să știe despre stratul de model și nu este responsabil cu manipularea datelor care provin din stratul modelului. Aceasta implică faptul că sursa de date a unei vizualizări de tabel sau orice altă componentă de vizualizare care implementează modelul de sursă de date este adesea un controler de un anumit tip. Pe iOS, este de obicei a UIViewController subclasă.

Semnătura metodei unui protocol de sursă de date respectă același model ca și protocolul unui delegat. Obiectul care trimite mesajele către sursa de date este trecut ca primul argument. Protocolul sursă de date trebuie să definească numai metode care se referă la datele care sunt utilizate de obiectul solicitant.

O vizualizare tabelă, de exemplu, întreabă sursa de date pentru numărul de secțiuni și rânduri pe care ar trebui să le afișeze. Dar, de asemenea, notifică sursa de date că un rând sau o secțiune a fost inserat sau șters. Acesta din urmă este important, deoarece sursa de date trebuie să se actualizeze pentru a reflecta modificările vizibile în vizualizarea tabelului. Dacă vizualizarea tabelului și sursa de date nu se sincronizează, se întâmplă lucruri rele.

3. Punerea în aplicare

Obiectiv-C

Implementarea modelului de delegat este destul de simplă acum, când înțelegem cum funcționează. Uitați-vă la următorul exemplu de Obiectiv-C.

#import  @protocol AddItemViewControllerDelegate; @interface AddItemViewController: UIViewController @property (slab, nonatomic) id delega; @end @ protocol AddItemViewControllerDelegate  - (void) viewControllerDidCancel: (AddItemViewController *) viewController; - (void) viewController: (AddItemViewController *) vizualizareController didAddItem: (NSString *) item; @optional - (BOOL) viewController: (AddItemViewController *) vizualizareController validateItem: (NSString *) element; @Sfârșit

Declarăm o clasă, AddItemViewController, care se extinde UIViewController. Clasa declară o proprietate, delega, de tip id. Rețineți că proprietatea este marcată ca slab, ceea ce înseamnă că o AddItemViewController instanța păstrează o referință slabă la delegatul său.

De asemenea, rețineți că am adăugat o declarație de protocol înaintea declarației de import a cadrului UIKit. Acest lucru este necesar pentru a evita avertizarea compilatorului. Am putea muta declarația de protocol sub declarația de import, dar prefer să o pun sub interfața de clasă. Aceasta nu este altceva decât o preferință personală.

Declarația protocolului este, de asemenea, destul de simplă. AddItemViewControllerDelegate protocol extinde NSObject protocol. Acest lucru nu este obligatoriu, dar se va dovedi foarte util. Vom afla de ce un pic mai târziu.

 AddItemViewControllerDelegate protocol declară două metode necesare și o metodă opțională. Așa cum am menționat mai devreme, este o practică bună să treci obiectul de delegare ca primul parametru al fiecărei metode de delegat pentru a informa delegatul care obiect trimite mesajul.

Metodele solicitate notifică delegatul despre un eveniment, o anulare sau o adăugare. Metoda opțională cere delegatului feedback. Se așteaptă ca delegatul să se întoarcă DA sau NU.

Aceasta este prima piesă a puzzle-ului delegației. Am declarat o clasă care declară a delega proprietate și am declarat un protocol delegat. A doua piesă a puzzle-ului invocă metodele delegate în AddItemViewController clasă. Să vedem cum funcționează.

În punerea în aplicare a directivei AddItemViewController clasa, punem în aplicare a Anulare: acțiune. Această acțiune ar putea fi conectată la un buton din interfața cu utilizatorul. Dacă utilizatorul fixează butonul, delegatul este informat despre acest eveniment și, în consecință, delegatul îl poate respinge AddItemViewController instanță.

- (IBAction) anulați: (id) expeditorul if (self.delegate && [self.delegate respondsToSelector: @selector (viewControllerDidCancel :)]) [auto.delegate viewControllerDidCancel: self]; 

Se recomandă să verificați dacă obiectul delegat nu este zero și că implementează metoda delegat pe care suntem pe cale să o invocăm, viewControllerDidCancel:. Acest lucru este ușor datorită respondsToSelector: metoda, declarată în NSObject protocol. Acesta este motivul pentru care AddItemViewControllerDelegate protocol extinde NSObject protocol. Prin extinderea NSObject protocol, obținem această funcționalitate gratuit.

Puteți să omiteți verificarea proprietății delegaților zero, de cand respondsToSelector: va reveni zero dacă proprietatea delegatului este zero. De obicei, adaug acest control, deoarece arată clar ce testează.

A treia și ultima piesă a puzzle-ului este implementarea protocolului delegat de către obiectul delegat. Următorul fragment de cod arată crearea unui AddItemViewController exemplu și implementarea uneia dintre metodele delegate.

- (IBAction) addItem: (id) expeditor // Initialize View Controller AddItemViewController * viewController = [[AddItemViewController alloc] init]; // Configurează controlerul de vizualizare [viewController setDelegate: self]; // Prezentator de vizualizare prezent [self presentViewController: viewController animat: DA finalizare: nil]; 
- (void) viewControllerDidCancel: (AddItemViewController *) viewController / / Dismați Add Item View Controller ...

Nu uitați să vă conformați clasei care acționează în calitate de delegat la AddItemViewControllerDelegate așa cum se arată mai jos. Puteți adăuga acest lucru în interfața de clasă sau într-o extensie de clasă privată.

#import "AddItemViewController.h" @interface ViewController ()  @Sfârșit

Rapid

În Swift, modelul delegării este la fel de ușor de implementat și veți descoperi că Swift face delegarea puțin mai elegantă. Să implementăm exemplul de mai sus în Swift. Aceasta este ceea ce AddItemViewController clasa arata ca in Swift.

import protocol UIKit AddItemViewControllerDelegate: NSObjectProtocol FUNC viewControllerDidCancel (viewController: AddItemViewController) FUNC viewController (viewController: AddItemViewController, didAddItem: String) FUNC viewController (viewController: AddItemViewController, validateItem: String) -> Bool class AddItemViewController: UIViewController var delegatului: AddItemViewControllerDelegate? func cancel (expeditor: AnyObject) delegate? .viewControllerDidCancel (self)

Declarația protocolului arată puțin diferit în Swift. Rețineți că AddItemViewControllerDelegate protocol extinde NSObjectProtocol in loc de NSObject protocol. În Swift, clasele și protocoalele nu pot avea același nume, motiv pentru care NSObject protocolul este denumit diferit în Swift.

delega proprietatea este o variabilă de tip AddItemViewControllerDelegate?. Observați semnul de întrebare la sfârșitul numelui protocolului. Proprietatea delegatului este opțională.

În Anulare(_:) metoda, invocăm viewControllerDidCancel (_ :) metoda delegată. Această singură linie arată cât de elegantă poate fi Swift. Desfacem în siguranță delega înainte de invocarea metodei delegate. Nu este nevoie să verificați dacă delegatul implementează viewControllerDidCancel (_ :) deoarece toate metodele unui protocol sunt necesare în Swift.

Să vedem acum ViewController clasa, care implementează AddItemViewControllerDelegate protocol. Interfața ne arată că ViewController clasa extinde UIViewController clasă și adoptă AddItemViewControllerDelegate protocol.

import funcția ViewController: UIViewController, AddItemViewControllerDelegate func addItem (trimite: AnyObject) // Initialize View Controller lăsați viewController = AddItemViewController () // Configurează controlerul ViewController.delegate = auto // Prezent View Viewer presentViewController (viewController, animated: true , completare: nil) func viewControllerDidCancel (viewController: AddItemViewController) // Dismuează adăugați elementul Vizualizați controlerul ... func viewController (viewController: AddItemViewController, didAddItem: String) functionController (viewController: AddItemViewController, validateItem: String) 

În adaugare element(_:) , inițializăm o instanță a AddItemViewController clasa, setați-o delega proprietate, și să o prezinte utilizatorului. Rețineți că am implementat metoda fiecărui delegat din AddItemViewControllerDelegate protocol. Dacă nu, compilatorul ne va spune că ViewController clasa nu este conformă cu AddItemViewControllerDelegate protocol. Încercați acest lucru comentând una dintre metodele delegate.

Concluzie

Delegarea este un model pe care îl veți întâlni frecvent atunci când dezvoltați aplicații pentru iOS și OS X. Cacao se bazează foarte mult pe acest model de design, astfel încât este important să vă familiarizați cu acesta.

De la introducerea blocurilor, cu câțiva ani în urmă, Apple a oferit încet un API bazat pe blocuri alternative la unele implementări ale delegațiilor. Unii dezvoltatori au urmat plumbul Apple oferind propriile alternative bazate pe blocuri. Biblioteca populară AFNetworking, de exemplu, se bazează foarte mult pe blocuri în loc de delegare, rezultând un API elegant și intuitiv.

Cod