Рассмотрим использование двух паттернов программирования в kotlin — singleton и doublecheck.
У нас есть пример кода, где экземпляр класса получается как singleton. Для асинхронного вызова функции используется блок синхронизации.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
abstract class MyClass { ... companion object { var instance: MyClass? = null private val LOCK = Any() fun getInstance(): MyClass { instance?.let { return it } synchronized(LOCK) { instance?.let { return it } val obj = ... instance = obj return obj } } } } |
Синглтон нужен в случаях, когда требуется использовать единственный экземпляр класса для всего приложения. Например, это может быть доступ к базе данных. При первом обращении через getInstance мы создадим экземпляр класса, а затем будем всегда возвращать этот экземпляр при любом обращении.
В методе getInstance мы также видим повторяющийся код :
1 2 3 |
instance?.let { return it } |
Его обычно называют паттерном doublecheck. Не всегда ясно зачем так делается, т.к. код будет верно работать и без дублирования. Мы можем оставить этот код внутри syncronized блока и приложение будет работать корректно.
В данном случае это делается для оптимизации производительности, чтобы предотвратить вход в synchronized блок. Вызов getInstance всегда, кроме первого раза должен просто вернуть существующий экземпляр класса MyClass.
Синхронизация может потребоваться только, если до создания экземпляра мы вызвали getInstance из 2х и более потоков.
В дальнейшем нам уже не требуется синхронизация, поэтому проверку instance на null предпочтительнее иметь вне блока синхронизации.