Singleton — qu'est-ce que c'est, une instance unique de classe dans iOS et Android

Auteur : IT Sectr Publié le : 2026-02-17 Temps de lecture : 8 min

Singleton — un pattern de création qui garantit une instance unique de classe et fournit un point d'accès global à celle-ci. Singleton est largement utilisé dans le développement mobile pour les ressources partagées : clients réseau, bases de données, gestionnaires de paramètres. Le pattern est décrit dans le livre classique du GoF (1994) et reste l'un des plus reconnaissables. En savoir plus sur Refactoring Guru : Singleton.

Points clés

  • Singleton — garantit une instance de classe par application
  • Point d'accès global — propriété statique shared ou companion object
  • Thread safety — synchronisation requise pour un fonctionnement correct en environnement multithread
  • Critique — Singleton complique les tests et crée des dépendances cachées
  • Alternatives — Dependency Injection, Service Locator pour remplacer Singleton

Qu'est-ce que Singleton : l'essence du pattern singleton ?

Singleton — un pattern de conception de création décrit par le GoF (Gang of Four) en 1994. Le pattern résout deux problèmes : il restreint l'instanciation d'une classe à un seul objet et fournit un accès global à cet objet. Singleton est utile pour les ressources qui doivent être uniques : fabriques de sessions, caches d'images, gestionnaires de connexion à la base de données, clients Crashlytics ou Analytics.

Implémentation de Singleton nécessite un constructeur privé (empêche la création externe), un champ statique avec l'instance unique et une méthode d'accès statique (shared, instance, getInstance). Les clients appellent Singleton.shared.method() sans se soucier de la création de l'objet. Le pattern est populaire dans iOS et Android : URLSession.shared, UserDefaults.standard, FirebaseApp.sharedInstance — tout cela est des Singletons. Cependant, l'utilisation excessive de Singleton mène à l'anti-pattern Global State.

Problèmes de Singleton — dépendances cachées (les classes dépendent implicitement de l'objet Singleton), complexité des tests (impossible de remplacer l'instance dans les tests sans effort supplémentaire), violation du Principe de Responsabilité Unique (Singleton gère à la fois son instance et la logique métier). Le développement mobile moderne préfère DI (Dagger, Hilt, Swinject) pour gérer les instances uniques — le conteneur DI crée l'objet une fois et l'injecte via le constructeur.

Singleton dans iOS avec Swift : shared et propriétés statiques

Swift Singleton est implémenté via une propriété statique shared avec un initialiseur privé. Depuis Swift 3, l'initialisation paresseuse des propriétés statiques est garantie thread-safe — le compilateur ajoute automatiquement la synchronisation via dispatch_once. Il suffit de déclarer static let shared = Class() et de rendre init() privé. Swift ne nécessite pas de synchronisation supplémentaire pour un accès mono-thread après l'initialisation.

swift
final class NetworkManager {
    // Singleton thread-safe
    static let shared = NetworkManager()

    private init() {
        URLSessionConfiguration.default.timeoutIntervalForRequest = 30
    }

    private var cache = NSCache<NSString, NSData>()

    func fetchData(from url: URL) async throws -> Data {
        let key = url.absoluteString as NSString
        if let cached = cache.object(forKey: key) {
            return cached as Data
        }
        let (data, _) = try await URLSession.shared.data(from: url)
        cache.setObject(data as NSData, forKey: key)
        return data
    }
}

// Utilisation
let data = try await NetworkManager.shared.fetchData(from: url)

Apple Singleton — de nombreux objets du SDK iOS utilisent Singleton : UIApplication.shared, UIScreen.main, FileManager.default, NotificationCenter.default, UserDefaults.standard. Apple utilise Singleton pour les services physiquement uniques (un écran, une application). Les développeurs copient ce pattern pour leurs propres services. Dans SwiftUI, l'accès global à Singleton est remplacé par Environment et @EnvironmentObject, améliorant la testabilité.

Singleton dans Android avec Kotlin : companion object et object

Kotlin Singleton — la façon la plus simple : le mot-clé object déclare une classe singleton avec initialisation paresseuse au premier accès. Kotlin object est thread-safe et ne nécessite pas de synchronisation supplémentaire. Si un Singleton avec paramètres de constructeur est nécessaire, on utilise un companion object avec un délégué lazy. Dans Android, Singleton est souvent nécessaire pour le contexte Application et les services qui s'initialisent via Application.onCreate().

kotlin
// Option 1 : object — Singleton simple sans paramètres
object AppPreferences {
    private val prefs = Application.instance
        .getSharedPreferences("app", Context.MODE_PRIVATE)

    var isFirstLaunch: Boolean
        get() = prefs.getBoolean("first_launch", true)
        set(value) = prefs.edit { putBoolean("first_launch", value) }
}

// Option 2 : companion object — Singleton avec paramètres
class ApiClient private constructor(baseUrl: String) {
    companion object {
        @Volatile
        private var instance: ApiClient? = null

        fun getInstance(baseUrl: String): ApiClient {
            return instance ?: this.synchronized {
                instance ?: ApiClient(baseUrl).also { instance = it }
            }
        }
    }

    fun request(endpoint: String): String { /* ... */ }
}

Singleton du SDK Android — de nombreux services système Android implémentent Singleton : context.getSystemService(), Room.databaseBuilder(), Retrofit.Builder(). Les exemples incluent SharedPreferences, MediaPlayer, AudioManager. Dans les applications Android, Singleton est souvent utilisé pour les repositories, gestionnaires et fabriques. Google recommande de remplacer Singleton par DI (Hilt, Koin), où la portée Singleton (Scope.Singleton ou @Singleton) est gérée par le conteneur tandis que la classe reste testable.

Thread safety : dispatch_once, synchronized et lock

Thread safety — une exigence critique pour Singleton dans les environnements multithread. Sans synchronisation, deux threads peuvent vérifier simultanément instance == null et créer deux instances. La solution est de verrouiller lors de la première création et de libérer après l'initialisation. Dans Swift, les propriétés statiques (static let) sont thread-safe par défaut. Dans Kotlin, object est thread-safe. Pour le style Java dans Kotlin, on utilise synchronized ou @Volatile + double-check locking.

LangageMécanismeSécurité des threadsInitialisation paresseuse
Swiftstatic letdispatch_once (automatique)Oui, au premier accès
Kotlin objectDéclaration d'objectInitialiseur de classe thread-safeOui, au premier accès
Kotlin companionsynchronized + @VolatileDouble-checked lockingOui, via lazy ou synchronized
Javasynchronized + volatileDouble-checked lockingOui, dans getInstance()

Double-checked locking — un pattern pour l'initialisation paresseuse de Singleton. Première vérification sans synchronisation (rapide si l'instance existe déjà), seconde à l'intérieur de synchronized (création par un seul thread). @Volatile garantit la visibilité des changements pour tous les threads. Sans volatile, un autre thread pourrait voir un objet partiellement construit. Dans Kotlin, le délégué lazy avec LazyThreadSafetyMode.SYNCHRONIZED implémente automatiquement le double-checked locking.

Singleton vs Dependency Injection : quand utiliser

Dependency Injection — une alternative à Singleton pour gérer les instances uniques. Un conteneur DI (Dagger, Hilt, Koin, Swinject) crée l'objet une fois dans une portée Singleton et l'injecte via le constructeur. La classe ne connaît pas son statut Singleton — le conteneur décide. Le code devient testable : le module DI est remplacé par un module mock dans les tests. Avantages de DI : dépendances explicites dans le constructeur, capacité de substitution, cycle de vie unifié.

Quand Singleton est justifié — objets au niveau système : Crashlytics, Analytics, Logging. Ces services sont initialisés une fois dans AppDelegate/Application et utilisés partout. DI est excessif pour eux. Singleton est également pratique pour les caches d'images (NSCache, Coil, Glide), où l'accès global est justifié par la performance. Pour tout le reste, DI est préférable : il rend les dépendances visibles, simplifie les tests et le refactoring.

Approche hybride — Singleton avec capacité de substitution pour les tests. Dans Swift, un protocole + propriété statique que les tests peuvent remplacer (par exemple, via URLProtocol pour URLSession). Dans Kotlin, une classe ouverte avec une propriété injectable, où les tests définissent un mock via réflexion ou un setter. Cette approche maintient la simplicité de Singleton mais offre des capacités de test. Google recommande Hilt pour Android, Apple n'impose pas DI pour iOS — le choix dépend de l'équipe.

Foire aux questions

Singleton est-il un anti-pattern ?

Non, Singleton est un pattern GoF, mais son utilisation incorrecte fréquente le transforme en anti-pattern Global State. Singleton est justifié pour les ressources physiquement uniques (écran, imprimante, système de fichiers). Les problèmes surviennent lorsque Singleton est utilisé pour la gestion de données : dépendances cachées, complexité des tests, violation du Principe de Responsabilité Unique. Une alternative moderne est DI avec portée Singleton.

Comment tester le code qui utilise Singleton ?

Trois approches : (1) via protocole — Singleton implémente un protocole, les tests échangent l'implémentation ; (2) via DI — Singleton est injecté comme dépendance via le constructeur ; (3) via méthode reset — Singleton a une méthode pour réinitialiser l'état dans les tests (uniquement pour les builds de test). La première approche est préférable, la troisième est dangereuse pour la production. Swift permet de remplacer la propriété shared via manipulation à l'exécution dans les tests.

En quoi Kotlin object diffère-t-il de Java Singleton ?

Kotlin object est une construction linguistique qui crée un Singleton au niveau bytecode. Contrairement à l'implémentation Java avec constructeur privé et getInstance(), object garantit la sécurité des threads, l'initialisation paresseuse et interdit l'héritage. Java Singleton nécessite une synchronisation manuelle (synchronized) et volatile pour un fonctionnement correct dans les environnements multithread. Kotlin object est la façon la plus sûre et la plus concise dans Android.

Singleton peut-il être hérité ?

Hériter de Singleton brise le pattern : si une classe Singleton peut être héritée, une sous-classe pourrait créer une deuxième instance, violant l'unicité. Dans Swift, final class interdit l'héritage. Kotlin object ne peut pas être hérité (object est sealed). Si un Singleton avec variabilité est nécessaire, utilisez un conteneur DI avec portée Singleton : il garantit une instance unique et supporte l'héritage via des interfaces.

Comment passer des paramètres à un Singleton dans Android ?

Les paramètres sont passés via init(context: Application) ou getInstance(param). Kotlin object n'accepte pas de paramètres — utilisez un companion object avec une méthode d'usine getInstance(param). Hilt résout le problème : @Singleton + @Inject constructor(context: Application) — le conteneur DI injecte le contexte Application automatiquement. Pour un client Retrofit, les paramètres (baseUrl, interceptors) sont passés via un builder dans le module DI.

Résumé

  • Singleton — un pattern avec instance unique et accès global
  • Swift shared — static let avec sécurité des threads garantie par le compilateur
  • Kotlin object — initialisation paresseuse sans code supplémentaire
  • Thread safety — double-checked locking pour Java, automatique pour Swift/Kotlin
  • SDK Apple — UIApplication.shared, UserDefaults.standard, FileManager.default
  • SDK Android — Retrofit, Room, SharedPreferences via gestionnaires Singleton
  • Alternatives — Dependency Injection pour un code testable

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi