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

Kotlin 싱글턴 패턴

Posted by 동식이 블로그 on June 3, 2020

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()
    }
}