Ontwerppatronen worden vaak beschouwd als een meer geavanceerd onderwerp en daarom genegeerd of over het hoofd gezien door mensen die nieuw zijn in iOS- of OS X-ontwikkeling. Er zijn echter een aantal ontwerppatronen die elke aspirant iOS- of OS X-ontwikkelaar vanaf dag één te zien zal krijgen. Het singleton-patroon is zo'n patroon.
Voordat we gaan kijken naar technieken om het singleton-patroon in Swift en Objective-C te implementeren, wil ik even stilstaan bij het singleton-patroon, inclusief de voor- en nadelen..
Het singleton-patroon is onlosmakelijk verbonden met objectgeoriënteerd programmeren. De definitie van het singleton-patroon is eenvoudig, er kan slechts één instantie van de singleton-klasse op elk willekeurig moment worden gemaakt of levend is.
Brent Simmons nuanceert deze definitie echter door er een onderscheid tussen te maken waar singletons en functioneel eenlingen. Brent definieert ware singletons als klassen die altijd dezelfde instantie van zichzelf teruggeven. Het is niet mogelijk om meer dan één instantie van de klasse te maken. Functionele singletons worden eenmaal gemaakt en op verschillende plaatsen gebruikt.
Ik weet zeker dat veel ontwikkelaars functionele singletons niet als singletons zouden indelen, maar ik vind het onderscheid dat Brent maakt wel leuk. Functionele singletons missen vaak de nadelen die kenmerkend zijn voor echte singletons. We zullen later in dit artikel naar deze nadelen kijken.
Als u bekend bent met de ontwikkeling van iOS of OS X, zult u merken dat Apple's frameworks gebruik maken van het singleton-patroon. Een iOS-applicatie kan bijvoorbeeld slechts één instantie van de UiApplication
klasse, die u kunt openen via de sharedApplication
klassemethode.
UIApplication * sharedApplication = [UIApplication sharedApplication];
laat sharedApplication = UIApplication.sharedApplication ()
Hoewel het UiApplication
klas geeft je toegang tot de UiApplication
singleton, niets weerhoudt je ervan expliciet een instantie te maken UiApplication
aanleg.
UIApplication * newApplication = [[UIApplication allocation] init];
laat newApplication = UIApplication ()
Het resultaat is echter een uitzondering op de runtime. De uitzondering stelt duidelijk dat slechts één instantie van de UiApplication
klas kan op elk moment in leven zijn. Met andere woorden, de UiApplication
klasse is ontworpen met het singleton-patroon in gedachten.
*** Beëindigen app vanwege niet-afgevangen uitzondering 'NSInternalInconsistencyException', reden: 'Er kan slechts één UIApplication-instantie zijn'.
Het meest voor de hand liggende voordeel van het singleton-patroon is toegankelijkheid. Houd rekening met de UiApplication
klasse als een voorbeeld. Door het UIKit-framework te importeren, is de UiApplication
singleton is overal in uw project toegankelijk. Bekijk het volgende voorbeeld van a UIViewController
subklasse.
#importeren@interface ViewController: UIViewController @end
#import "ViewController.h" @implementation ViewController #pragma mark - #pragma mark View Life Cycle - (void) viewDidLoad [super viewDidLoad]; // Access Application Singleton UIApplication * application = [UIApplication sharedApplication]; @end
Het is soms nuttig of essentieel dat slechts één instantie van een klasse op een bepaald moment in leven is. De UiApplication
klas is een voorbeeld van deze vereiste. Andere voorbeelden kunnen klassen zijn voor logboekregistratie, caching of I / O-bewerkingen.
Dat gezegd hebbende, is het belangrijk om te begrijpen dat singletons een manier zijn om controle en gedrag te bereiken. Defensieve coderingstechnieken kunnen hetzelfde resultaat opleveren zonder de mentale overhead van het singleton-patroon.
Door een singleton te maken, creëer je in essentie een globale staat. Het is belangrijk dat je je hiervan bewust bent. Een paar jaar geleden schreef Miško Hevery een geweldig artikel over het singleton-patroon waarin hij uitlegt waarom het patroon moet worden vermeden. Miško benadrukt dat globale toestand de belangrijkste reden is waarom singletons schadelijk zijn.
Er zijn echter uitzonderingen. Bijvoorbeeld singletons die onveranderlijk zijn, kunnen weinig schade aanrichten. Je bent nog steeds bezig een globale staat te creëren, maar die staat is onveranderlijk. Miško noemt ook loggers als een ietwat acceptabele toepassing van het singleton-patroon, omdat de toestand in één richting stroomt, van uw toepassing in de logger.
De meeste objectgeoriënteerde programmeertalen beschouwen een nauwe koppeling van modules als een slechte praktijk. Anders gezegd, het wordt aanbevolen om modules zoveel mogelijk te ontkoppelen. Dit plaatst het singleton-patroon op een slechte plaats en singletons worden daarom door veel gerespecteerde programmeurs als een slechte praktijk beschouwd. Sommige ontwikkelaars gaan zelfs zover dat ze een antipatroon beschouwen dat moet worden vermeden.
Raadpleeg het volgende scenario om het probleem beter te begrijpen. U hebt een iOS-applicatie gemaakt met een netwerkcomponent waarin u toegang hebt tot de UiApplication
Singleton. Uw iOS-applicatie is zo'n succes dat u overweegt een OS X-toepassing te maken. De UiApplication
klasse is echter niet beschikbaar op OS X vanwege het ontbreken van het UIKit-framework. Dit betekent dat u de netwerkcomponent van de iOS-applicatie moet refactoren of wijzigen.
Er zijn een aantal oplossingen om dit probleem op te lossen, maar het punt dat ik wil maken, is dat je de netwerkmodule nauw hebt gekoppeld aan het UIKit-raamwerk, de UiApplication
klasse in het bijzonder.
Strakke koppeling is vaak nauw verbonden met herbruikbaarheid. Een strak gekoppelde objectgrafiek is vaak moeilijk opnieuw te gebruiken zonder te refactoren. Slechte herbruikbaarheid is soms onvermijdelijk, maar het is zeker iets om in gedachten te houden bij het ontwikkelen van software.
Een singleton maken in Objective-C is heel eenvoudig. In het volgende codefragment maken en implementeren we een logging-klasse, houthakker
. We benaderen het singleton-object via de sharedLogger
klassemethode.
#importeren@interface Logger: NSObject #pragma mark - #pragma mark Class Methods + (Logger *) sharedLogger; @einde
De implementatie is eenvoudig. We verklaren een statische variabele _sharedInstance
die een verwijzing naar het singleton-object bevat. We roepen dispatch_once
, een Grand Central Dispatch functie, en geef een blok door waarin we een instantie van de houthakker
klasse. We slaan een verwijzing op naar de instantie in _sharedInstance
.
#import "Logger.h" @implementation Logger # pragma mark - #pragma mark Klasse Methoden + (Logger *) sharedLogger static Logger * _sharedInstance = nil; static dispatch_once_t oncePredicate; dispatch_once (& oncePredicate, ^ _sharedInstance = [[self allocation] init];); return _sharedInstance; @end
De dispatch_once
functie zorgt ervoor dat het blok dat we passeren eenmaal wordt uitgevoerd gedurende de levensduur van de applicatie. Dit is erg belangrijk omdat we slechts één instantie van de .nl willen initialiseren houthakker
klasse.
De oncePredicate
variabele die wordt doorgegeven als het eerste argument van de dispatch_once
functie wordt gebruikt door de dispatch_once
functie om ervoor te zorgen dat het blok slechts eenmaal wordt uitgevoerd.
De sharedLogger
klassemethode retourneert de houthakker
bijvoorbeeld door de referentie te retourneren die is opgeslagen in _sharedInstance
. De implementatie van sharedLogger
kan aanvankelijk ontmoedigend lijken, maar ik weet zeker dat u het ermee eens bent dat het idee heel eenvoudig is.
Om het singleton-patroon in Swift te implementeren, maken we gebruik van klasseconstanten, die zijn geïntroduceerd in Swift 1.2. Als u problemen ondervindt met het onderstaande codefragment, zorg er dan voor dat u Xcode 6.3 gebruikt+.
class Logger static let sharedInstance = Logger ()
Laat me je begeleiden door de korte implementatie van de houthakker
klasse. We beginnen met het declareren van een klasse met de naam houthakker
. Om het singleton-patroon te implementeren, verklaren we een type-eigenschap, sharedInstance
, van type houthakker
.
Door de statisch
trefwoord, de sharedInstance
constant is verbonden met de houthakker
class in plaats van instanties van de klasse. De sharedInstance
constante wordt een a genoemd type eigenschap omdat het is gekoppeld aan het type, dat wil zeggen, het houthakker
klasse. We benaderen het singleton-object via de houthakker
klasse.
Logger.sharedInstance
U vraagt zich misschien af waarom we de dispatch_once
functioneren zoals we deden in de Objective-C-implementatie. Onder de motorkap gebruikt Swift al dispatch_once
bij het initialiseren van statische constanten. Dat is iets dat je gratis krijgt in Swift.
Een andere veel voorkomende aanpak voor het implementeren van het singleton-patroon is door gebruik te maken van geneste typen. In het volgende voorbeeld declareren we een eigenschap van het computertype, sharedInstance
, van type houthakker
. Bij de afsluiting van de eigenschap van het computertype verklaren we een structuur met de naam eenling
. De structuur definieert een eigenschap van het constante type, aanleg
, van type houthakker
.
class Logger class var sharedInstance: Logger struct Singleton static let instance: Logger = Logger () retourneer Singleton.instance
Toegang tot het singleton-object is hetzelfde voor beide strategieën. Als u Swift 1.2 gebruikt, raad ik aan de eerste benadering te gebruiken vanwege de beknoptheid en eenvoud.
Ik veronderstel dat het verleidelijk is, als het enige gereedschap dat je hebt een hamer is, om alles te behandelen alsof het een spijker is. - Abraham Maslow
Toen ik voor het eerst over het singleton-patroon hoorde, was ik enthousiast over hoe nuttig het zou zijn in mijn volgende project. Het is erg verleidelijk om een tool te gebruiken die veel problemen lijkt op te lossen. Op het eerste gezicht lijkt het singleton-patroon op een zilveren kogel of een gouden hamer, maar dat is niet wat het singleton-patroon is.
Als u bijvoorbeeld een singleton alleen gebruikt om deze gemakkelijk toegankelijk te maken, gebruikt u mogelijk het singleton-patroon om de verkeerde redenen. Niet omdat Apple het singleton-patroon in zijn eigen frameworks gebruikt, maar het is de juiste oplossing voor elk probleem.
Singletons kunnen erg nuttig zijn, maar ze worden door veel ervaren programmeurs beschouwd als een anti-patroon. De meesten van hen hebben slechte ervaringen met het patroon om tot die conclusie te komen. Leer van hun fouten en gebruik ze spaarzaam.
Ik beschouw het singleton-patroon niet als een anti-patroon, maar het is een patroon dat met voorzichtigheid moet worden gebruikt. Als je de verleiding voelt om een object in een singleton te veranderen, stel jezelf dan de vraag of er een andere manier is om het probleem op te lossen. In de meeste gevallen is het singleton-patroon niet de enige beschikbare oplossing. Een alternatieve oplossing kan meer werk of een beetje overhead kosten, maar het is waarschijnlijk beter op de lange termijn.
De functionele singletons waarnaar Brent Simmons verwijst, zijn vaak een perfect bruikbaar alternatief voor echte singletons. Er is niets mis met het maken van een instantie van een klasse en deze door te geven aan de objecten die het nodig hebben. Overweeg deze benadering de volgende keer dat u over het maken van nog een singleton gaat.
Het singleton-patroon is eenvoudig en krachtig, maar het moet spaarzaam worden gebruikt. Sommige van uw collega's kunnen u hiertegen adviseren, maar ik denk dat het belangrijk is om het gebruik ervan te overwegen en zelf te beoordelen of u denkt dat het een nuttige aanvulling op uw gereedschapskist is..