[kotlin] 싱글톤 singleton

neoneoneo·2024년 4월 4일
0

kotlin

목록 보기
47/49

싱글톤?

  • 앱에서 하나의 인스턴스만을 갖도록 보장하는 객체
  • 클래스의 인스턴스가 오직 한 번만 생성되고, 그 인스턴스에 대해서는 전역적으로 접근이 가능해진다.

장점

  • 리소스 낭비 최소화
    • 인스턴스가 한 번만 생성되기 때문에 메모리 및 자원의 낭비를 방지한다.
  • 공유 리소스 접근 용이
    • 여러 곳에서 동일한 인스턴스에 접근할 수 있으므로 데이터의 일관성을 유지할 수 있다.
  • 전역적인 접근성
    • 어떤 클래스에서든 인스턴스에 쉽게 접근할 수 있다.

사용법 1. 객체 선언

object MySingleton {
    init {
        // 초기화 코드
    }
    fun doSomething() {
        // 싱글톤 객체에서 수행할 작업
    }
}
  • 싱글톤 오브젝트를 선언해둔다.
MySingleton.doSomething()
  • 인스턴스를 생성해서 뭔가를 수행할 때에는 이렇게 작성한다.

사용법 2. Companion Object

먼저 Companion Object가 뭔지?

클래스 내부에 선언되는 정적 멤버를 관리하는 데 사용

클래스 인스턴스 없이 해당 클래스의 인스턴스 멤버에 접근하거나 호출할 수 있도록 한다.

멤버를 클래스 이름을 통해 직접 호출할 수 있다.

Companion Object를 사용하는 법

class MyClass {
    companion object {
        fun myFunction() {
            println("Companion object의 함수입니다.")
        }
    }
}
  • 필요할 때 var my = myFunction() 으로 호출할 수 있다.

Companion Object 내부에 프로퍼티와 생성자를 정의할 수 있다.

class MyClass {
	companion object {
    	val myProperty: Int = 10
        private var count: Int = 0       
        init {
        	printlnln("Companion Object의 초기화 블록")
        }
        fun incrementCount() {
        	count++
        }
        fun getCount(): Int {
        	return count
        }
        fun createInstance(value: String): MyClass {
        	return MyClass(value)
        }
    }
    private constructor(value: String) {
    	println("인스턴스 생성: $value")
    }
}
fun main() {
	println("프로퍼티 값: ${MyClass.myProperty}") //10 출력
    MyClass.incrementCount() //count = 1
    println("카운트 값: ${MyClass.getCount()}") //11 출력
    val instance = MyClass.createInstance("Hello") //인스턴스 생성: Hello 출력
}

자 그럼, 다시 Companion Object로 싱글톤을 구현하는 부분을 이해해보자.

class MySingleton private constructor() {
	init {
    	println("싱글톤 인스턴스 생성")
    }
    companion object {
    	@Volatile
        private var instance: MySingleton? = null
        fun getInstance(): MySingleton {
        	return instance ?: synchronized(this) {
            	instance ?: MySingleton().also { instance = it }
            }
        }
    }
    fun doSomgthing() {
    	println("싱글톤이 뭔가를 한다")
    }
}
fun main() {
	val singleton1 = MySingleton.getInstance() //싱글톤 인스턴스 생성 출력
    val singleton2 = MySingleton.getInstace() //아무것도 출력되지 않음
    println(sigleton1 == singleton2) //true 출력
    singleton1.doSomething() //싱글톤이 뭔가를 한다 출력
}
  • singleton2에서 getInstance()를 해도, 이미 singleton1에서 인스턴스를 생성해놨기 때문에 초기화 블록(init) 내부의 코드가 실행되지 않는다.

여기서 synchronized를 사용한 것은 다중 스레드 환경에서의 안정성을 보장하기 위한 것이며, lazy를 쓰는 방법도 있다.

class MySingleton private constructor() {
    init {
        println("Singleton instance created")
    }
    companion object {
        val instance: MySingleton by lazy { MySingleton() }
    }
    fun doSomething() {
        println("Singleton is doing something")
    }
}
fun main() {
    val singleton1 = MySingleton.instance
    val singleton2 = MySingleton.instance
    println(singleton1 === singleton2) // true
    singleton1.doSomething()
}

sychronized, lazy 부분 없이 object만 쓰거나 companion object만 사용해도 싱글톤을 구현할 수 있다.

class MySingleton private constructor() {
    init {
        println("Singleton instance created")
    }
    companion object {
        val instance = MySingleton()
    }
    fun doSomething() {
        println("Singleton is doing something")
    }
}
fun main() {
    MySingleton.instance.doSomething()
}

[TIL-240404]

0개의 댓글