동식이 블로그 / dongsik93.github.io
TIL 2 MIN READ 429 WORDS

[Kotlin] 디자인패턴 (3) - 싱글턴 패턴

Kotlin 싱글턴 패턴

#kotlin #android

Kotlin 객체지향 디자인 패턴

Java 객체지향 디자인 패턴 책을 보고 Kotlin으로 변환하면서 공부한 내용입니다

Java객체지향 디자인패턴

6장. 싱글턴 패턴

  1. 싱글턴 패턴이란

  • 인스턴스가 오직 하나만 생성되는 것을 보장하고 어디에서는 이 인스턴스에 접근할 수 있도록 하는 디자인 패턴
  • 정적메서드로만 이루어진 정적 클래스를 사용해도 동일한 효과를 얻을 수 있다

6-1

  • Singleton
    • 하나의 인스턴스만을 생성하는 책임이 있으며 getInstance 메서드를 통해 모든 클라이언트에게 동일한 인스턴스를 반환하는 작업을 수행한다
  1. 프린터 관리자 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class User(private val name: String) {
    fun print() {
        val printer = Printer.getPrinter()
        printer.print(this.name + " print using " + printer.toString() + ".")
    }
}

class Printer private constructor() {
    companion object {
        private lateinit var printer: Printer
        
        fun getPrinter(): Printer {
            if (!::printer.isInitialized) printer = Printer()
            return printer
        }
    }
    
    fun print(str: String) {
        println(str)
    }
}

fun main() {
    val user = MutableList(5) { i -> i }
    user.forEach { userName ->
        val users = User((userName + 1).toString() + "-user")
        users.print()
    }
}

main()
  1. 문제점

  • 다중 스레드에서 Printer 클래스를 이용할 때 인스턴스가 1개 이상 생성되는 경우가 발생할 수 있다
    • 경합조건이 발생
      • 메모리와 같은 동일한 자원을 2개 이상의 스레드가 이용하려고 경합하는 현상
  1. 해결책

  • 정적 변수에 인스턴스를 만들어 바로 초기화하는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Printer private constructor() {
    private var counter = 0
    companion object {
        private var printer: Printer = Printer()
        
        fun getPrinter(): Printer {
            return printer
        }
    }
    
    fun print(str: String) {
        counter++
        println(str)
    }
}
  • 인스턴스를 만드는 메서드에 동기화하는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class User(name: String) : Thread(name) {
    override fun run() {
        val printer = Printer.getPrinter()
        printer.print(this.name + " print using " + printer.toString() + ".")
    }
}

class Printer private constructor() {
    private var counter = 0
    
    companion object {
        private lateinit var printer: Printer
        
        @Synchronized
        fun getPrinter(): Printer {
            if (!::printer.isInitialized) {
                try {
                    Thread.sleep(10)
                } catch (e: InterruptedException) { }
                printer = Printer()
            }
            return printer
        }
    }
    
    fun print(str: String) {
        // 오직 하나의 스레드만 접근을 허용하기 위해
        synchronized(this) {
            counter++
            println(str + counter)
        }
    }
}

fun main() {
    val user = MutableList(5) { i -> i }
    user.forEach { userName ->
        val users = User((userName + 1).toString() + "-user")
        users.start()
    }
}