Singletons i Java

1. Introduksjon

I denne raske artikkelen vil vi diskutere de to mest populære måtene å implementere Singletons på vanlig Java.

2. Klassebasert Singleton

Den mest populære tilnærmingen er å implementere en Singleton ved å lage en vanlig klasse og sørge for at den har:

  • En privat konstruktør
  • Et statisk felt som inneholder den eneste forekomsten
  • En statisk fabrikkmetode for å skaffe forekomsten

Vi legger også til en info-egenskap, kun for senere bruk. Så implementeringen vår vil se slik ut:

offentlig endelig klasse ClassSingleton {privat statisk ClassSingleton INSTANCE; private String info = "Initial info class"; privat ClassSingleton () {} offentlig statisk ClassSingleton getInstance () {hvis (INSTANCE == null) {INSTANCE = ny ClassSingleton (); } returner INSTANCE; } // getters og setters}

Selv om dette er en vanlig tilnærming, er det viktig å merke seg at det kan være problematisk i flertrådingsscenarier, som er hovedårsaken til bruk av Singletons.

Enkelt sagt, det kan resultere i mer enn en forekomst, som bryter mønsterets kjerneprinsipp. Selv om det er låsingsløsninger på dette problemet, løser vår neste tilnærming disse problemene på rotnivå.

3. Enum Singleton

La oss ikke diskutere en annen interessant tilnærming - som er å bruke enumerasjoner:

offentlig enum EnumSingleton {INSTANCE ("Initial class info"); privat strenginfo; private EnumSingleton (strenginfo) {this.info = info; } offentlig EnumSingleton getInstance () {return INSTANCE; } // getters og setters}

Denne tilnærmingen har seriellisering og trådsikkerhet garantert av selve enum-implementeringen, noe som sikrer internt at bare den eneste forekomsten er tilgjengelig, og korrigerer problemene som påpekes i den klassebaserte implementeringen.

4. Bruk

Å bruke vår ClassSingleton, vi trenger bare å få forekomsten statisk:

ClassSingleton classSingleton1 = ClassSingleton.getInstance (); System.out.println (classSingleton1.getInfo ()); // Innledende klasseinformasjon ClassSingleton classSingleton2 = ClassSingleton.getInstance (); classSingleton2.setInfo ("Ny klasseinfo"); System.out.println (classSingleton1.getInfo ()); // Ny klasseinformasjon System.out.println (classSingleton2.getInfo ()); // Ny klasseinfo

Når det gjelder EnumSingleton, kan vi bruke den som alle andre Java Enum:

EnumSingleton enumSingleton1 = EnumSingleton.INSTANCE.getInstance (); System.out.println (enumSingleton1.getInfo ()); // Initial enum info EnumSingleton enumSingleton2 = EnumSingleton.INSTANCE.getInstance (); enumSingleton2.setInfo ("Ny enum info"); System.out.println (enumSingleton1.getInfo ()); // Ny enum info System.out.println (enumSingleton2.getInfo ()); // Ny enum info

5. Vanlige fallgruver

Singleton er et villedende enkelt designmønster, og det er få vanlige feil som en programmerer kan begå når de lager en singleton.

Vi skiller to typer problemer med singler:

  • eksistensiell (trenger vi en singleton?)
  • implementerings (implementerer vi det riktig?)

5.1. Eksistensielle problemer

Konseptuelt er en singleton en slags global variabel. Generelt vet vi at globale variabler bør unngås - spesielt hvis tilstandene deres er mutable.

Vi sier ikke at vi aldri skal bruke singletoner. Vi sier imidlertid at det kan være mer effektive måter å organisere koden på.

Hvis implementeringen av en metode avhenger av et enkeltobjekt, hvorfor ikke sende det som en parameter? I dette tilfellet viser vi eksplisitt hva metoden avhenger av. Som en konsekvens kan vi lett spotte disse avhengighetene (om nødvendig) når vi utfører testing.

For eksempel brukes singletons ofte til å omfatte programmets konfigurasjonsdata (dvs. tilkobling til depotet). Hvis de brukes som globale objekter, blir det vanskelig å velge konfigurasjonen for testmiljøet.

Derfor, når vi kjører testene, blir produksjonsdatabasen bortskjemt med testdataene, noe som knapt er akseptabelt.

Hvis vi trenger en singleton, kan vi vurdere muligheten for å delegere instantiering til en annen klasse - en slags fabrikk - som skal sørge for at det bare er en forekomst av singleton i spill.

5.2. Implementeringsspørsmål

Selv om singletonene virker ganske enkle, kan implementeringene deres lide av ulike problemer. Alt resulterer i at vi kan ende opp med å ha mer enn bare en forekomst av klassen.

Synkronisering

Implementeringen med en privat konstruktør som vi presenterte ovenfor er ikke trådsikker: den fungerer bra i et enkelt trådmiljø, men i en multigjenget, bør vi bruke synkroniseringsteknikken for å garantere atomisiteten i operasjonen:

offentlig synkronisert statisk ClassSingleton getInstance () {if (INSTANCE == null) {INSTANCE = new ClassSingleton (); } returner INSTANCE; }

Legg merke til nøkkelordet synkronisert i metodedeklarasjonen. Metodens kropp har flere operasjoner (sammenligning, instantiering og retur).

I mangel av synkronisering er det en mulighet for at to tråder fletter inn henrettelsene på en slik måte at uttrykket INSTANS == null evaluerer til ekte for begge tråder og som et resultat av to forekomster av ClassSingleton bli opprettet.

Synkronisering kan påvirke ytelsen betydelig. Hvis denne koden blir påkalt ofte, bør vi øke hastigheten ved å bruke forskjellige teknikker som lat initialisering eller dobbeltsjekket låsing (vær oppmerksom på at dette ikke fungerer som forventet på grunn av kompilatoroptimaliseringer). Vi kan se flere detaljer i vår veiledning “Dobbeltsjekket låsing med Singleton“.

Flere forekomster

Det er flere andre problemer med singletons relatert til selve JVM som kan føre til at vi ender med flere forekomster av en singleton. Disse problemene er ganske subtile, og vi vil gi en kort beskrivelse for hver av dem:

  1. En singleton skal være unik per JVM. Dette kan være et problem for distribuerte systemer eller systemer hvis interne er basert på distribuert teknologi.
  2. Hver klasselaster kan laste sin versjon av singleton.
  3. En singleton kan bli samlet inn søppel når ingen har en referanse til den. Dette problemet fører ikke til tilstedeværelsen av flere singleton-forekomster om gangen, men når det gjenskapes, kan forekomsten avvike fra den forrige versjonen.

6. Konklusjon

I denne raske opplæringen fokuserte vi på hvordan du implementerer Singleton-mønsteret ved hjelp av bare Java-kjernen, og hvordan du kan sikre at det er konsistent og hvordan du kan bruke disse implementeringene.

Fullstendig implementering av disse eksemplene finner du på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found