← Назад к вопросам

Приведи пример реализации паттерна Singleton

2.2 Middle🔥 121 комментариев
#JVM и память#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Реализация паттерна Singleton в Kotlin для Android

Singleton — это порождающий паттерн проектирования, который гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к этому экземпляру. В Android этот паттерн часто используется для менеджеров, сервисов, хранилищ и других компонентов, которые должны существовать в единственном экземпляре.

Основные реализации Singleton в Kotlin

1. Простейшая реализация через object (рекомендуемый способ)

Kotlin предоставляет встроенную поддержку Singleton через ключевое слово object:

object NetworkManager {
    private const val TAG = "NetworkManager"
    
    init {
        // Инициализация при первом обращении
        println("NetworkManager initialized")
    }
    
    fun connect() {
        println("Connecting to network...")
    }
    
    fun disconnect() {
        println("Disconnecting from network...")
    }
}

Использование:

// Где-то в коде приложения
NetworkManager.connect()

2. Singleton с ленивой инициализацией (Lazy)

class DatabaseManager private constructor() {
    companion object {
        // Используем ленивую инициализацию
        val instance: DatabaseManager by lazy {
            println("DatabaseManager lazy initialization")
            DatabaseManager()
        }
    }
    
    init {
        println("DatabaseManager constructor called")
    }
    
    fun query(sql: String): List<Any> {
        println("Executing query: $sql")
        return emptyList()
    }
}

Преимущества:

  • Ленивая инициализация — экземпляр создается только при первом обращении
  • Потокобезопасностьby lazy по умолчанию потокобезопасен
  • Простота реализации

3. Thread-safe Singleton с двойной проверкой (Double-Checked Locking)

class SettingsManager private constructor(context: Context) {
    companion object {
        @Volatile
        private var instance: SettingsManager? = null
        
        fun getInstance(context: Context): SettingsManager {
            // Первая проверка (без блокировки)
            if (instance == null) {
                synchronized(this) {
                    // Вторая проверка (с блокировкой)
                    if (instance == null) {
                        instance = SettingsManager(context.applicationContext)
                    }
                }
            }
            return instance!!
        }
    }
    
    private val sharedPreferences: SharedPreferences
    
    init {
        sharedPreferences = context.getSharedPreferences("app_settings", Context.MODE_PRIVATE)
    }
    
    fun saveSetting(key: String, value: Any) {
        when (value) {
            is String -> sharedPreferences.edit().putString(key, value).apply()
            is Int -> sharedPreferences.edit().putInt(key, value).apply()
            is Boolean -> sharedPreferences.edit().putBoolean(key, value).apply()
        }
    }
    
    fun getSetting(key: String, defaultValue: Any): Any {
        return sharedPreferences.all[key] ?: defaultValue
    }
}

Использование в Android Activity/Fragment:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // Получение экземпляра Singleton
        val settingsManager = SettingsManager.getInstance(this)
        settingsManager.saveSetting("theme", "dark")
    }
}

4. Parameterized Singleton с зависимостью от контекста

class AnalyticsTracker private constructor(
    private val context: Context,
    private val apiKey: String
) {
    companion object {
        @Volatile
        private var instance: AnalyticsTracker? = null
        
        fun initialize(context: Context, apiKey: String) {
            if (instance == null) {
                synchronized(this) {
                    if (instance == null) {
                        instance = AnalyticsTracker(context.applicationContext, apiKey)
                    }
                }
            }
        }
        
        fun getInstance(): AnalyticsTracker {
            return instance ?: throw IllegalStateException(
                "AnalyticsTracker must be initialized first. Call initialize()"
            )
        }
    }
    
    fun trackEvent(eventName: String, properties: Map<String, Any> = emptyMap()) {
        // Реализация отслеживания событий
        Log.d("Analytics", "Event: $eventName, Properties: $properties")
    }
}

Инициализация в Application классе:

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        
        // Инициализация Singleton
        AnalyticsTracker.initialize(
            context = this,
            apiKey = BuildConfig.ANALYTICS_API_KEY
        )
    }
}

Рекомендации для Android разработки

Плюсы использования Singleton:

  • Глобальный доступ к общему состоянию
  • Контролируемая инициализация ресурсоемких объектов
  • Сокращение дублирования кода

Минусы и предостережения:

  • Нарушение принципа инверсии зависимостей — затрудняет тестирование
  • Потенциальная утечка памяти в Android (особенно при хранении Context)
  • Сложность с жизненным циклом в многомодульных приложениях

Альтернативы для Android:

// 1. Внедрение зависимостей через Dagger/Hilt
@Singleton
@Component(modules = [NetworkModule::class])
interface AppComponent {
    fun inject(activity: MainActivity)
}

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}

// 2. Использование ViewModel для хранения состояния
class SharedViewModel : ViewModel() {
    val liveData = MutableLiveData<String>()
}

// В Activity/Fragment
val viewModel: SharedViewModel by viewModels()

Best Practices для Android

  1. Используйте Application Context вместо Activity Context
  2. Очищайте ресурсы при необходимости
  3. Избегайте хранения View или Activity ссылок
  4. Рассмотрите альтернативы типа Dependency Injection
  5. Документируйте потокобезопасность вашей реализации
// Пример Singleton с очисткой ресурсов
object ResourceManager : Closeable {
    private val resources = mutableMapOf<String, Any>()
    
    fun acquireResource(key: String): Any {
        // Логика получения ресурса
        return resources.getOrPut(key) { createResource(key) }
    }
    
    override fun close() {
        // Очистка ресурсов
        resources.clear()
        println("ResourceManager resources cleaned up")
    }
    
    private fun createResource(key: String): Any {
        // Создание ресурса
        return "Resource_$key"
    }
}

Выбор реализации Singleton зависит от конкретных требований приложения. Для большинства случаев в Kotlin достаточно использовать object или by lazy, а для сложных сценариев с параметрами инициализации — потокобезопасные реализации с двойной проверкой.