동식이 블로그 / dongsik93.github.io
TIL 4 MIN READ 665 WORDS

[Kotlin] 디자인패턴 (6) - 옵저버 패턴

Kotlin 스테이트 패턴

#kotlin #android

Kotlin 객체지향 디자인 패턴

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

Java객체지향 디자인패턴

9장. 옵저버 패턴

  1. 옵저버 패턴이란?

  • 데이터의 변경이 발생했을 경우 상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 유용하다
  • 통보 대상 객체의 관리를 Subject 클래스와 Observer 인터페이스로 일반화한다
    • 데이터 변경을 통보하는 클래스는 통보 대상 클래스나 객체에 대한 의존성을 없앨 수 있다
    • 결과적으로 옵저버 패턴은 통보 대상 클래스나 대상 객체의 변경에도 ConcreteSubject 클래스를 수정 없이 그대로 사용할 수 있도록 한다

9-1

  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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.lang.Integer.min

class ScoreRecord {
    private val scores: MutableList<Int> = mutableListOf()
    private lateinit var dataSheetView: DataSheetView
    
    fun setDataSheetView(dataSheetView: DataSheetView) {
        this.dataSheetView = dataSheetView
    }
    
    fun addScore(score: Int) {
        scores.add(score)
        dataSheetView.update()
    }
    
    fun getScoreRecord() : MutableList<Int> = scores
}

class DataSheetView(private val scoreRecord: ScoreRecord, private val viewCount: Int) {
    fun update() {
        val record = scoreRecord.getScoreRecord()
        displayScores(record, viewCount)
    }
    
    fun displayScores(record: MutableList<Int>, viewCount: Int) {
        println("list of $viewCount entries: ")
        for(i in  0 until min(record.size, viewCount)) {
            println(record[i])
        }
        println()
    }
}

fun client() {
    val scoreRecord = ScoreRecord()
    
    val dataSheetView = DataSheetView(scoreRecord, 3)
    scoreRecord.setDataSheetView(dataSheetView)
    
    for(i in 0 until 6) {
        val score = i * 10
        println("adding $score")
        scoreRecord.addScore(score)
    }
}
  1. 문제점

  • 성적을 다른 형태로 출력하고 싶다면?
  • 여러 가지 형태의 성적을 동시 혹은 순차적으로 출력하려면?
  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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import java.lang.Integer.min
import java.util.*

interface Observer {
    abstract fun update()
}

abstract class Subject {
    private val observers: MutableList<Observer> = mutableListOf()
    fun attach(observer: Observer){
        observers.add(observer)
    }
    
    fun detach(observer: Observer) {
        observers.remove(observer)
    }
    // 통보 대상 목록, 즉 각 옵저버에게 변경을 통보
    fun notifyObserver() {
        observers.forEach {o ->
            o.update()
        }
    }
}

class ScoreRecord : Subject() {
    private val scores: MutableList<Int> = mutableListOf()
    fun addScore(score: Int) {
        scores.add(score)
        notifyObserver()
    }

    fun getScoreRecord() : MutableList<Int> = scores
}


class DataSheetView(private val scoreRecord: ScoreRecord, private val viewCount: Int) : Observer {
    // 통보 받음
    override fun update() {
        val record = scoreRecord.getScoreRecord()
        displayScores(record, viewCount)
    }
    // 변경 통보 시 리스트 목록 출력
    fun displayScores(record: MutableList<Int>, viewCount: Int) {
        println("list of $viewCount entries: ")
        for(i in  0 until min(record.size, viewCount)) {
            println(record[i])
        }
        println()
    }
}

class MinMaxView(private val scoreRecord: ScoreRecord) : Observer {
    // 통보 받음
    override fun update() {
        val record = scoreRecord.getScoreRecord()
        displayMinMax(record)
    }
    // 변경 통보 시 최소값, 최대값 출력
    fun displayMinMax(record: MutableList<Int>) {
        val minValue = Collections.min(record, null)
        val maxValue = Collections.max(record, null)
        println("Min: $minValue , Max : $maxValue")
    }
}

// StatisticsView는 Observer를 구현함으로써 통보 대상이 됨 
class StatisticsView(private val scoreRecord: ScoreRecord) : Observer {
    // 통보 받음
    override fun update() {
        val record = scoreRecord.getScoreRecord()
        displayStatistics(record)
    }
    // 변경 통보 시 조회된 점수의 합과 평균을 출력함
    fun displayStatistics(record: MutableList<Int>) {
        var sum = 0
        record.forEach { score ->
            sum += score
            val average: Float = (sum / record.size).toFloat()
            println("Sum: $sum, Average: $average")
        }
    }
}

fun client() {
    val scoreRecord = ScoreRecord()
    
    val dataSheetView = DataSheetView(scoreRecord, 3)
    scoreRecord.attach(dataSheetView)
    val minMaxView = MinMaxView(scoreRecord)
    scoreRecord.attach(minMaxView)
    
    for(i in 1 until 6) {
        val score = i * 10
        println("adding $score")
        scoreRecord.addScore(score)
    }
    
    scoreRecord.detach(dataSheetView)
    val statisticsView = StatisticsView(scoreRecord)
    scoreRecord.attach(statisticsView)
    
    for(i in 1 until 6) {
        val score = i * 10
        println("adding $score")
        scoreRecord.addScore(score)
    }
}

client()