Hei! I dag skal vi dykke ned i detaljene i ulike designmønstre, og starter med Java Singleton-mønsteret. La oss gå gjennom: hva vet vi om designmønstre generelt? Designmønstre er beste praksis som vi kan bruke for å løse en rekke kjente problemer. Designmønstre er generelt ikke knyttet til noe programmeringsspråk. Tenk på dem som et sett med anbefalinger for å hjelpe deg med å unngå feil og unngå å finne opp hjulet på nytt.Designmønstre: Singleton - 1

Hva er en singleton i Java?

Singleton er et av de enkleste designmønstrene på klassenivå. Noen ganger sier folk "denne klassen er singleton", som betyr at klassen implementerer singleton-designmønsteret. Noen ganger er det nødvendig å skrive en klasse der vi begrenser instansiering til et enkelt objekt. For eksempel en klasse som er ansvarlig for å logge eller koble til en database. Singleton-designmønsteret beskriver hvordan vi kan oppnå dette. En singleton er et designmønster som gjør to ting:
  1. Det garanterer at det bare vil være én forekomst av klassen.

  2. Det gir et enkelt punkt med global tilgang til den forekomsten.

Derfor er det to funksjoner som er karakteristiske for nesten hver implementering av singleton-mønsteret:
  1. En privat konstruktør. Dette begrenser muligheten til å lage objekter av klassen utenfor selve klassen.

  2. En offentlig statisk metode som returnerer forekomsten av klassen. Denne metoden kalles getInstance . Dette er poenget med global tilgang til klasseforekomsten.

Implementeringsmuligheter

Singleton-designmønsteret brukes på forskjellige måter. Hvert alternativ er bra og dårlig på sin egen måte. Som alltid er det ikke noe perfekt alternativ her, men vi bør strebe etter et. Først av alt, la oss bestemme hva som er bra og dårlig, og hvilke beregninger som påvirker hvordan vi vurderer de ulike implementeringene av designmønsteret. La oss starte med det gode. Her er faktorer som gjør en implementering mer saftig og tiltalende:
  • Lazy initialisering: forekomsten opprettes ikke før den er nødvendig.

  • Enkel og gjennomsiktig kode: denne beregningen er selvfølgelig subjektiv, men den er viktig.

  • Gjengesikkerhet: korrekt drift i et flertrådsmiljø.

  • Høy ytelse i et flertrådsmiljø: liten eller ingen trådblokkering når du deler en ressurs.

Nå ulempene. Vi lister opp faktorer som setter en implementering i et dårlig lys:
  • Ingen lat initialisering: når klassen lastes når applikasjonen starter, uavhengig av om den er nødvendig eller ikke (paradoksalt nok, i IT-verdenen er det bedre å være lat)

  • Kompleks og vanskelig å lese kode. Denne beregningen er også subjektiv. Hvis øynene dine begynner å blø, antar vi at implementeringen ikke er den beste.

  • Mangel på trådsikkerhet. Med andre ord "trådfare". Feil drift i et flertrådsmiljø.

  • Dårlig ytelse i et flertrådsmiljø: tråder blokkerer hverandre hele tiden eller ofte når de deler en ressurs.

Kode

Nå er vi klare til å vurdere ulike implementeringsalternativer og angi fordeler og ulemper:

Enkel


public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {
    }
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}
Den enkleste implementeringen. Fordeler:
  • Enkel og gjennomsiktig kode

  • Trådsikkerhet

  • Høy ytelse i et flertrådsmiljø

Ulemper:
  • Ingen lat initialisering.
I et forsøk på å fikse den forrige mangelen får vi implementering nummer to:

Lat initialisering


public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Fordeler:
  • Lat initialisering.

Ulemper:
  • Ikke trådsikker

Denne implementeringen er interessant. Vi kan initialisere dovent, men vi har mistet trådsikkerheten. Ingen grunn til bekymring – vi synkroniserer alt i implementering nummer tre.

Synkronisert tilgang


public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Fordeler:
  • Lat initialisering.

  • Trådsikkerhet

Ulemper:
  • Dårlig multithreaded ytelse

Utmerket! I implementering nummer tre gjenoppretter vi trådsikkerheten! Selvfølgelig er det tregt... Nå er getInstance- metoden synkronisert, så den kan kjøres av bare én tråd om gangen. I stedet for å synkronisere hele metoden, trenger vi faktisk bare å synkronisere den delen av den som initialiserer den nye forekomsten. Men vi kan ikke bare bruke en synkronisert blokk for å pakke inn delen som er ansvarlig for å lage den nye forekomsten. Å gjøre det ville ikke sikre trådsikkerheten. Det hele er litt mer komplisert. Riktig synkronisering kan sees nedenfor:

Dobbeltsjekket låsing


public class Singleton {
    private static final Singleton INSTANCE;

  private Singleton() {
  }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
Fordeler:
  • Lat initialisering.

  • Trådsikkerhet

  • Høy ytelse i et flertrådsmiljø

Ulemper:
  • Støttes ikke i tidligere versjoner av Java under 1.5 (bruken av flyktige nøkkelord er løst siden 1.5-versjonen)

Merk at for at dette implementeringsalternativet skal fungere riktig, må en av to betingelser være oppfylt. Variabelen INSTANCE må enten være endelig eller flyktig . Den siste implementeringen som vi skal diskutere i dag er klasseholderen singleton .

Klasseholder


public class Singleton {

   private Singleton() {
   }

   private static class SingletonHolder {
       public static final Singleton HOLDER_INSTANCE = new Singleton();
   }

   public static Singleton getInstance() {
       return SingletonHolder.HOLDER_INSTANCE;
   }
}
Fordeler:
  • Lat initialisering.

  • Trådsikkerhet.

  • Høy ytelse i et flertrådsmiljø.

Ulemper:
  • Riktig drift krever en garanti for at singleton- objektet initialiseres uten feil. Ellers vil det første kallet til getInstance -metoden resultere i en ExceptionInInitializerError , og alle påfølgende anrop vil produsere en NoClassDefFoundError .

Denne implementeringen er nesten perfekt. Den er lat, og trådsikker og rask. Men det har en nyanse, som forklart i listen over ulemper. Sammenligning av ulike implementeringer av singleton-mønsteret:
Gjennomføring Lat initialisering Trådsikkerhet Multithreaded ytelse Når skal du bruke?
Enkel - + Fort Aldri. Eller muligens når lat initialisering ikke er viktig. Men ville aldri vært bedre.
Lat initialisering + - Ikke aktuelt Alltid når flertråding ikke er nødvendig
Synkronisert tilgang + + Langsom Aldri. Eller muligens når multithreaded ytelse ikke spiller noen rolle. Men ville aldri vært bedre.
Dobbeltsjekket låsing + + Fort I sjeldne tilfeller når du trenger å håndtere unntak når du oppretter singleton (når klasseholderen singleton ikke er aktuelt)
Klasseholder + + Fort Når multithreading er nødvendig og det er en garanti for at singleton-objektet vil bli opprettet uten problemer.

Fordeler og ulemper med singleton-mønsteret

Generelt gjør en singleton akkurat det som forventes av den:
  1. Det garanterer at det bare vil være én forekomst av klassen.

  2. Det gir et enkelt punkt med global tilgang til den forekomsten.

Imidlertid har dette mønsteret mangler:
  1. En singleton bryter med enkeltansvarsprinsippet: i tillegg til sine direkte plikter kontrollerer singleton-klassen også antall instanser.

  2. En vanlig klasses avhengighet av en singleton er ikke synlig i klassens offentlige kontrakt.

  3. Globale variabler er dårlige. Til syvende og sist blir en singleton til en heftig global variabel.

  4. Tilstedeværelsen av en singleton reduserer testbarheten til applikasjonen som helhet og klassene som bruker singletonen spesielt.

Og det er det! :) Vi har utforsket Java Singleton Class med deg. Nå, for resten av livet, når du snakker med programmerervennene dine, kan du ikke bare nevne hvor bra mønsteret er, men også noen få ord om hva som gjør det dårlig. Lykke til med å mestre denne nye kunnskapen.

Ytterligere lesning: