Modele de design Singletons

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.

1. Ce este un Singleton?

Trupe adevărate și funcționale

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".

avantaje

Accesibilitate

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

Control și comportament

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.

Dezavantaje

Statul global

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.

Strâns cuplaj

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.

reutilizabilitate

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.

2. Crearea unui Singleton

Obiectiv-C

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ă.

Rapid

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.

3. Când se utilizează Singletons?

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.

Concluzie

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.

Cod