Modelele de design sunt adesea considerate un subiect mai avansat și, prin urmare, sunt ignorate sau trecute cu vederea de către oameni noi pentru dezvoltarea iOS sau OS X. Cu toate acestea, există un șir de modele de design pe care fiecare aspirant dezvoltator iOS sau OS X se va confrunta cu prima zi. Modelul singleton este un astfel de model.
Înainte de a începe să ne uităm la tehnicile de implementare a modelului singleton în Swift și Obiectiv-C, aș dori să iau un moment pentru a înțelege modelul singleton, inclusiv avantajele și dezavantajele acestuia.
Modelul singleton este legat în mod inextricabil de programarea orientată obiect. Definiția modelului singleton este simplă, doar o singură instanță a clasei singleton poate fi creată sau este în viață la un moment dat.
Brent Simmons, însă, nuanțează această definiție făcând o distincție între Adevărat singletle și funcţional singletons. Brent definește singletonele adevărate ca clase care întotdeauna returnează aceeași instanță a ei înșiși. Nu este posibilă crearea a mai mult de o instanță a clasei. Singleturile funcționale sunt create odată și folosite în mai multe locuri.
Sunt sigur că mulți dezvoltatori nu vor clasifica singletonele funcționale ca singletons, dar îmi place distincția făcută de Brent. Singletul funcțional lipsește deseori dezavantajele tipice celor singletoni adevărați. Ne vom uita la aceste dezavantaje mai târziu în acest articol.
Dacă sunteți familiarizați cu dezvoltarea iOS sau OS X, atunci veți observa că cadrele Apple folosesc modelul singleton. O aplicație iOS, de exemplu, poate avea doar o instanță a UIApplication
clasa, pe care puteți accesa prin sharedApplication
clasă.
UIApplication * sharedApplication = [UIApplication sharedApplication];
let sharedApplication = UIApplication.sharedApplication ()
Chiar dacă UIApplication
vă oferă acces la UIApplication
singleton, nimic nu vă împiedică să instanțiați în mod explicit o UIApplication
instanță.
Aplicația UIA * newApplication = [[UIAplication alin] init];
lasati newApplication = UIApplication ()
Rezultatul, totuși, este o excepție în timpul runtime-ului. Excepția afirmă în mod clar că doar o singură instanță a UIApplication
clasa poate fi în viață la un moment dat. Cu alte cuvinte, UIApplication
clasa a fost proiectat cu modelul singleton în minte.
*** Terminarea aplicației din cauza excepției "NSInternalInconsistencyException", motivat: "Nu poate exista decât o instanță UIA aplicație".
Cel mai evident avantaj al modelului singleton este accesibilitatea. Considera UIApplication
clasa ca un exemplu. Prin importarea cadrului UIKit, UIApplication
singleton este accesibil de oriunde în proiect. Aruncati o privire la urmatorul exemplu de a UIViewController
subclasă.
#import@ interfață ViewController: UIViewController @end
#import "ViewController.h" @implementation ViewController #pragma - marca #pragma Vizualizați ciclul de viață - (void) viewDidLoad [super viewDidLoad]; // Aplicație de acces Singleton UIApplication * application = [UIApplication sharedApplication]; @Sfârșit
Uneori este util sau esențial ca numai o singură instanță a unei clase să fie în viață la un moment dat. UIApplication
clasa este un exemplu al acestei cerințe. Alte exemple pot include clase pentru logare, cache, sau operații I / O.
Acestea fiind spuse, este important să înțelegeți că singletonii sunt un mijloc de a realiza controlul și comportamentul. Tehnicile de codificare defensivă vă pot oferi același rezultat fără supraviețuirea mentală a modelului singleton.
Prin crearea unui singleton, în esență, creați statul global. Este important să știți asta. Cu câțiva ani în urmă, Miško Hevery a scris un articol extraordinar despre modelul singleton în care explică de ce modelul ar trebui evitat. Miško subliniază faptul că statul global este principalul motiv pentru care singletonurile sunt dăunătoare.
Există însă excepții. De exemplu, singletonele care sunt imuabile pot face puțin rău. Încă mai creați statul global, dar acel stat este imuabil. Miško menționează, de asemenea, loggerii ca o aplicație oarecum acceptabilă a modelului singleton, deoarece statul curge într-o direcție, de la aplicația dvs. în logger.
Majoritatea limbajelor de programare orientate pe obiect consideră o cuplare strânsă a modulelor o practică proastă. Puneți altfel, se recomandă decuplarea modulelor cât mai mult posibil. Acest lucru pune modelul singleton într-un loc rău și singletons sunt, prin urmare, considerate o practică proastă de către mulți programatori respectate. Unii dezvoltatori merg în măsura în care consideră că este un tip anti-model care ar trebui evitat.
Pentru a înțelege mai bine problema, luați în considerare următorul scenariu. Ați creat o aplicație iOS cu o componentă de rețea în care accesați UIApplication
Singleton. Aplicația dvs. pentru iOS este un astfel de succes pe care vă gândiți să creați o aplicație OS X. UIApplication
clasa, cu toate acestea, nu este disponibilă pe OS X din cauza lipsei cadrului UIKit. Aceasta înseamnă că va trebui să refaceți sau să modificați componenta de rețea a aplicației iOS.
Există o serie de soluții pentru a rezolva această problemă, dar punctul pe care vreau să îl fac este că ați cuplat strâns modulul de rețea în cadrul UIKit, UIApplication
clasa în special.
Conectarea strânsă este adesea strâns asociată cu reutilizarea. Un graf obiect strâns cuplat este foarte dificil de reutilizat fără refactorizare. Reutilizarea slabă este uneori inevitabilă, dar este cu siguranță ceva de reținut atunci când se dezvoltă software-ul.
Crearea unui singleton în Obiectiv-C este foarte ușor de făcut. În următorul fragment de cod, creăm și implementăm o clasă de logare, Logger
. Accesăm obiectul singleton prin sharedLogger
clasă.
#import@ Logger interfață: NSObject #pragma - marca #pragma Clasă Metode + (Logger *) sharedLogger; @Sfârșit
Implementarea este simplă. Declarăm o variabilă statică _sharedInstance
care va conține o referință la obiectul singleton. Noi invocăm dispatch_once
, A Grand Central Dispatch funcția și treceți într-un bloc în care inițializăm o instanță a Logger
clasă. Stocăm o referință la instanță în _sharedInstance
.
#import "Logger.h" @implementare Logger #pragma - marca #pragma Clasa Metode + (Logger *) sharedLogger static Logger * _sharedInstance = nil; static dispatch_once_t oncePredicate; dispatch_once (& oncePredicate, ^ _sharedInstance = [[self alloc] init];); returnați _sharedInstance; @Sfârșit
dispatch_once
Funcția asigură că blocul pe care îl transmitem este executat o singură dată pentru durata de viață a aplicației. Acest lucru este foarte important deoarece nu dorim decât să inițializăm o instanță a Logger
clasă.
oncePredicate
variabilă care este trecută ca primul argument al dispatch_once
funcția este utilizată de către dispatch_once
pentru a se asigura că blocul este executat o singură dată.
sharedLogger
metoda de clasă returnează Logger
exemplu prin returnarea referinței stocate în _sharedInstance
. Implementarea sistemului sharedLogger
pot părea descurajante la început, dar sunt sigur că sunteți de acord că ideea este foarte simplă.
Pentru a implementa modelul singleton în Swift, folosim constantele de clasă introduse în Swift 1.2. Dacă aveți probleme cu fragmentul de cod de mai jos, asigurați-vă că utilizați Xcode 6.3+.
clasa Logger static let sharedInstance = Logger ()
Permiteți-mi să vă prezint prin scurta punere în aplicare a Logger
clasă. Începem prin a declara o clasă numită Logger
. Pentru a implementa modelul singleton, declarăm o proprietate de tip, sharedInstance
, de tip Logger
.
Prin utilizarea funcției static
cuvânt cheie, sharedInstance
constantă este legată de Logger
clasă în locul instanțelor din clasă. sharedInstance
constanta este numită a tip proprietate deoarece este legat de tipul, adică de Logger
clasă. Accesăm obiectul singleton prin Logger
clasă.
Logger.sharedInstance
S-ar putea să te întrebi de ce nu folosim dispatch_once
cum am făcut-o în implementarea Obiectiv-C. Sub capotă, Swift utilizează deja dispatch_once
când se inițiază constante statice. Asta e ceva pe care îl poți beneficia gratuit în Swift.
O altă abordare comună pentru implementarea modelului singleton este utilizarea unor tipuri imbricate. În exemplul următor, declarăm o proprietate de tip calculată, sharedInstance
, de tip Logger
. La închiderea proprietății de tip computerizat, declarăm o structură numită Singleton
. Structura definește o proprietate de tip constant, instanță
, de tip Logger
.
clasa Logger clasa var sharedInstance: Logger struct Singleton statică exemplu: Logger = Logger () retur Singleton.instance
Accesarea obiectului singleton este aceeași pentru ambele strategii. Dacă utilizați Swift 1.2, vă recomandăm să folosiți prima abordare pentru coerența și simplitatea sa.
Presupun că este tentant, dacă singurul instrument pe care îl aveți este un ciocan, să tratați totul ca și cum ar fi un cui. - Abraham Maslow
Când am auzit prima dată despre modelul singleton, am fost încântat de cât de util ar fi în următorul meu proiect. Este foarte tentant să folosiți un instrument care pare să rezolve numeroase probleme. La prima vedere, modelul singleton poate arăta ca un glonț de argint sau un ciocan de aur, dar asta nu este modelul singleton.
De exemplu, dacă utilizați un singleton doar pentru a fi ușor accesibil, atunci este posibil să utilizați modelul singleton din motive greșite. Nu pentru că Apple folosește modelul singleton în cadrul propriu, că este soluția potrivită pentru fiecare problemă.
Singleturile pot fi foarte utile, dar sunt considerate un model anti-model de către mulți programatori experimentați. Cei mai mulți dintre ei au avut experiențe nefavorabile cu modelul de a ajunge la această concluzie. Aflați de la greșelile lor și le folosiți cu ușurință.
Nu consider modelul singleton un model anti-model, dar este un model care ar trebui folosit cu prudență. Dacă vă simțiți tentați să transformați un obiect într-un singur ton, întrebați-vă dacă există vreo altă cale prin care să rezolvați problema. În majoritatea cazurilor, modelul singleton nu este singura soluție disponibilă. O soluție alternativă poate necesita mai multă muncă sau puțină cheltuială, dar este probabil mai bună pe termen lung.
Singleturile funcționale pe care Brent Simmons le face se referă adesea la o alternativă perfect valabilă față de adevărații singletoni. Nu este nimic în neregulă cu crearea unei instanțe a unei clase și trecerea acesteia către obiectele care au nevoie de ea. Luați în considerare această abordare data viitoare când sunteți despre crearea unui alt singleton.
Modelul singleton este simplu și puternic, dar ar trebui folosit cu ușurință. Unii dintre colegii dvs. vă pot sfătui împotriva acestui lucru, dar cred că este important să luați în considerare utilizarea acestuia și să judecați pentru dvs. dacă credeți că este un plus util pentru setul dvs. de instrumente.