Singleton (Singelton)
Introduktion
Singleton är ett skapande mönster som säkerställer att en klass endast har en instans och tillhandahåller en global åtkomstpunkt till den.
Problem
Singleton-mönstret löser två problem samtidigt (vilket tekniskt sett bryter mot Single Responsibility Principle):
- En enda instans — Viss kod kräver exakt en instans av en klass, t.ex. en databasanslutning eller ett konfigurationsobjekt. Skapar du flera instanser riskerar du race conditions, inkonsekvent data och onödig resursåtgång.
- Global åtkomst — Objektet ska vara åtkomligt från var som helst i programmet, men skyddad från att skrivas över av misstag.
Lösning
Alla Singleton-implementationer delar dessa två egenskaper:
- Privat konstruktor — Förhindrar att andra klasser skapar instanser med
new. - Statisk åtkomstmetod — En metod som skapar instansen vid första anropet och returnerar den cachade instansen vid alla efterföljande anrop.
När ska Singleton användas?
- När en klass i ditt program bara ska ha en enda instans för alla klienter.
- T.ex. delad databasanslutning, logg-system, konfigurationshanterare, connection pool.
Struktur
| Del | Beskrivning |
|---|---|
| Privat konstruktor | Hindrar direkt instansiering. |
| Statiskt privat fält | Lagrar den enda instansen. |
Statisk getInstance() | Skapar (lazy) eller returnerar den befintliga instansen. |
Exempel — Databasanslutning (Java)
Scenariot: En databasklass som garanterar att bara en anslutning skapas, även i flertrådade miljöer.
public final class Database {
// Det statiska fältet för den enda instansen
private static volatile Database instance;
private String connectionString;
// Privat konstruktor — ingen kan anropa new Database()
private Database(String connectionString) {
// Simulera en tidskrävande initiering
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); }
this.connectionString = connectionString;
System.out.println("Databas initierad: " + connectionString);
}
// Thread-safe lazy initialization med double-checked locking
public static Database getInstance(String connectionString) {
if (instance == null) {
synchronized (Database.class) {
if (instance == null) {
instance = new Database(connectionString);
}
}
}
return instance;
}
public void query(String sql) {
System.out.println("[" + connectionString + "] Kör: " + sql);
}
}
// ── Klientkod ─────────────────────────────────────────────
public class Application {
public static void main(String[] args) {
Database db1 = Database.getInstance("jdbc:mysql://localhost/bytebase");
Database db2 = Database.getInstance("jdbc:mysql://localhost/bytebase");
System.out.println("Samma instans? " + (db1 == db2)); // true
db1.query("SELECT * FROM patterns");
db2.query("INSERT INTO patterns VALUES (...)");
// Båda anropen går mot SAMMA anslutning
}
}
Output:
Databas initierad: jdbc:mysql://localhost/bytebase
Samma instans? true
[jdbc:mysql://localhost/bytebase] Kör: SELECT * FROM patterns
[jdbc:mysql://localhost/bytebase] Kör: INSERT INTO patterns VALUES (...)
Singleton i Spring Boot
I modern Spring Boot-utveckling behöver du sällan implementera Singleton manuellt. Spring hanterar detta via sin IoC-container — alla @Component- och @Service-klasser är som standard Singletons:
@Service
public class DatabaseService {
// Spring garanterar att bara EN instans skapas i applikationskontexten
public void query(String sql) { /* ... */ }
}
Fördelar
- Garanterar att en klass bara har en instans.
- Global åtkomstpunkt till instansen.
- Instansen initieras bara när den faktiskt behövs (lazy initialization).
Nackdelar
- Kan dölja dålig design — komponenter som “vet för mycket” om varandra.
- Kräver extra hantering i flertrådade miljöer.
- Svårt att enhetstest — Singletons bär tillstånd mellan tester.
Av Victor Hernandez från Bytebase.se