Singleton provider

Tags:    .net
Skrevet af Bruger #285 @ 23.01.2007
I C# 2.0 har vi fået de dejlige generics. Vi har stadig behov for at lave singletonklasser. Hvorfor dog ikke benytte disse og lave en generisk klasse, som klasser kan nedarve fra, når de skal være singletonklasser. På den måde opnår man endnu bedre indkapsling!

Så lad os se på det. Vi har på forhånd et par problemer. Vi skal på en eller anden måde kunne instantiere subklassen fra superklassen, men for at opnå singletonfunktionalitet vil vi ikke have en public constructor. Derfor bliver vi nødt til at benytte os af reflection i superklassen. Med reflektion kan man bl.a. kalde en privat contructor, men man behøver ikke sikre, at der ikke også er en public constructor (godt nok skal den være overloaded i forhold til den private, men det vil stadig muliggøre yderligere instantiering), men da vi allerede har gang i reflektion, kan vi lige så godt i samme omgang sikre, at der udelukkende er non-public constructors i vores subklasse. Lad os se på lidt kode. Først er vores subklasse og hvordan vi ønsker at den skal se ud (og virke):
Fold kodeboks ind/udKode 


Vi kan dog lige så godt indbygge lidt så vi kan teste løsningen:
Fold kodeboks ind/udKode 


Følgende kode kan vi bruge til at teste med:
Fold kodeboks ind/udKode 


Og nu til vores centrale generiske klasse, der sørger for alt trylleriet:
Fold kodeboks ind/udKode 


Lad os tage forklaringen lidt blandet. For det første benytter vi os af en static constructor. På tilsvarende måde som alm. construktorer virker på instanser, virker en static på selve klassen. Instans-constructoren er protected, da den ikke skal være public da det ville gå imod singletonmønstret, og den kan heller ikke være private, da det så ikke ville muliggøre den nedarvning, som vi ønsker at benytte os af til at opnå målet. Vi benytter os af propertien Instance til at give adgang til instances.

Og nu til "magien"/reflectiondelen: foreach-løkken sørger for at hente information om alle constructorer i T. Hvis en af disse er public, kastes en exception og derfor bliver der ikke instantieret noget. Herefter henter vi info om hvorvidt der er en default non-public constructor. Hvis ikke reflektionen kan finde en sådan, kastes på samme måde som før en exception. Hvis der findes en, bliver der i try-statementen forsøgt at oprette en instans af T.

Inspirationen til overstående er kommet fra et besøg på følgende side:
http://www.codeproject.com/csharp/FunWithSingletonsCS.asp
En af problemerne ved denne er, at de ikke tjekker for om klassen udover en default non-public constructor har en vilkår public construktor, og derved kan idéen med det hele ryge, hvis man i klassen tilbyder en sådan (eller i hvert fald får lov til det).

I stedet for ConstructorInfo.Invoke er det også muligt at bruge Activator.CreateInstance, men dels er den jf. http://blogs.msdn.com/haibo_luo/archive/2005/11/17/494009.aspx langsommere, og dels får man så ikke mulighed for at tjekke for overstående vedr. public constructors.

Links til dokumentation:
http://msdn2.microsoft.com/en-US/library/system.reflection.constructorinfo.aspx
http://msdn2.microsoft.com/en-us/library/system.Activator.CreateInstance.aspx


Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (7)

User
Bruger #285 @ 25.01.07 10:01
Hej. Jeg kan se mine nye artikler bliver stemt til 1 og 2. Det er selvfølgelig helt okay, men I må hjertens gerne lige knytte en kommentar til hvorfor, således jeg har mulighed for at ændre det i fremtiden. Hvis I vil være anonyme, kan I bare sende det til KasperTSW og sige det, så kan han vidregive det til mig uden jeres navn. Men altså: jeg har intet imod lave bedømmelser, jeg vil bare gerne have en mulighed for at kunne gøre det bedre fremover.
User
Bruger #3491 @ 31.01.07 12:19
Så vidt jeg kan se forklarer du slet ikke hvad en "singletonklasse" er og hvornår man kunne tænke sig at bruge sådan en. Jeg tror egentlig ikke der er specielt mange der vil lære noget af denne artikel. Ok, man får et lille indblik i hvad reflection kan det er sådan set det.

Jeg undrer mig også lidt over din kode, hvorfor overhovedet tjekke om der er nogle public constructorer? Sådan noget ville jeg nok placere i min test-kode.
Hvorfor try...catch...throw? Hvordan giver det mening?
Hvorfor er den ikke abstract?

Hvorfor overhovedet bruge tid på at lave sådan en klasse? At gøre en klasse singleton er så lille et indgreb at det stort set ikke kan betale sig.
User
Bruger #285 @ 31.01.07 13:19
Hej Martin. Nivauet for denne artikel er sat til "Lettere øvet", og dermed er det nok antaget, at folk her ved hvad en Singleton er (eller meget nemt kan læse sig til det). Denne artikel er ikke for at argumentere for idéen bag singleton, men blot komme med et indspark til hvordan klasser kan laves singleton uden at have duplikeret kode.

Grunden til at der tjekkes for public constructor(s) er jo netop fordi, at hvis man ønsker at en klasse skal være singleton (og tilgås gennem denne provider), så må den ikke have en public constructor (det vil jo bryde med singleton-mønstret).

Og ja, det er nemt at gøre én klasse singleton, men forestil dig at du har fx 15 klasser, der alle skal være singleton. Det er meget træls at lave så meget ens kode. Og det er lige så meget for at give en lille idé om, hvordan man kan gøre det - og at det faktisk i nogle tilfælde er muligt at "fuske lidt" :-)
User
Bruger #285 @ 31.01.07 13:20
Og den er ikke abstract fordi man så ikke ville kunne bruge den. Når den er abstrakt, skal man nedarve fra den og så benytte disse subklasser.
User
Bruger #7954 @ 25.02.07 14:10
Din singleton er ikke trådsikker...

Blot fordi noget er abstract betyder ikke at du ikke kan have implementation deri. Du nedarver jo alligevel, og lægger op til at nedarve fra din singleton klasse, så du kan ligeså godt lave den abstract. Så SKAL der nedarves fra den, hvilket giver væsentligt bedre design.

Og den der try-catch-throw er ganske enkelt vederstyggelig!

Jeg vil forslå dig at læse lidt mere om Singleton pattern her:
http://en.wikipedia.org/wiki/Singleton_pattern
User
Bruger #285 @ 22.04.07 20:44
Kevin Johansen: Ja, jeg vil give dig helt ret, hvis man har en almindelig Singleton, men denne er lavet til hvis man har brug for nedarvning fra andet end en Singleton-"arbejderklasse". Ofte ønsker man - rent design- og konceptuelt at benytte en anden superklasse end en arbejderklasse, som en Singleton er. Og det er her, denne provider gør sig anvendelig. Men som sagt, har du ret i det du siger, hvis man normalt har brug for en Singleton. Dette er ikke forklaret i dybden i artikel, hvilket uden tvivl er med til at gøre det mere uklart. Jeg beklager.
User
Bruger #11812 @ 11.05.07 13:39
Nu er jeg jo godt nok en ny bruger her på sitet, men jeg vil sige at dine artikler indtil nu har manglet noget kød.

Du siger selv at dine artikler er for "Lettere øvede" .. Singleton Pattern kan du kun introducere hvis du fortæller hvad det er. Hvis du ikke fortæller hvad det er skal du absolut lave en kommentar til hvor man kan finde ud af hvad det er, ellers refererer du noget ingen har en anelse om, som lettere øvede.

Jeg vil anbefale "Head First Design Patterns" fra Oreilly. Her får man ikke kun SIngleton Pattern'et beskrevet, men bjerge af andre patterns. For dem der ikke ved hvad et "pattern" er, er det en art standardiserede måder at udføre noget generelt arbejde på.

Singleton's fornemmeste opgave er at sørge for at der kun eksisterer EN og kun EN instantiering af en klasse i en applikation.
Du skal være logget ind for at skrive en kommentar.
t