๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐ŸงฉDesign Patterns

[Design Patterns]Observer pattern, ๋ณ€ํ™”์— ๋ฐ˜์‘

by ๋นˆ์„ฑ_ 2022. 5. 16.
๋ฐ˜์‘ํ˜•

Intro

์˜ค๋Š˜์€ ๊ฐ„๋‹จํ•˜๊ฒŒ Observer ํŒจํ„ด์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•ด๋ดค์Šต๋‹ˆ๋‹ค. Observer ํŒจํ„ด์€ ์–ด๋–ค ๊ฐ์ฒด๊ฐ€ ๋ณ€ํ–ˆ์„ ๋•Œ ๋“ฑ๋ก๋œ(ํ˜น์€ ๋ณด๊ณ  ์žˆ๋˜) ๊ด€์ฐฐ์ž(Observer)๋“ค์—๊ฒŒ ํ†ต์ง€ํ•˜๋Š”, Behavioral Pattern์ž…๋‹ˆ๋‹ค.

Kotlin์—์„œ๋Š” Observer pattern์„ ๋‘ ๊ฐ€์ง€์˜ ํ˜•ํƒœ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜๋Š” Standard, ๊ทธ๋Ÿฌ๋‹ˆ๊นŒ ํ‰๋ฒ”ํ•œ ๋ชจ์Šต์œผ๋กœ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•˜๊ณ , ํ•˜๋‚˜๋Š” Kotlin์˜ observable delagates๋ฅผ ํ™œ์šฉํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

Standard

๋จผ์ € Standardํ•˜๊ฒŒ ๊ตฌํ˜„ํ•œ ๋ชจ์Šต์ž…๋‹ˆ๋‹ค. Observer ๊ด€์ฐฐํ•˜๊ณ  ์žˆ๋Š” ๊ฐ์ฒด๊ฐ€ ๋ณ€ํ–ˆ์„ ๋•Œ Observer์˜ update๊ฐ€ ๋™์ž‘ํ•˜๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค.

interface Observer {

    fun update()
}

Observable์€ Observer๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. Observer๋ฅผ add์™€ remove ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋“ฑ๋กํ•˜๊ณ , ๋“ฑ๋ก๋œ Observer๋“ค์—๊ฒŒ ๊ฐ์ฒด๊ฐ€ ๋ณ€ํ™”ํ–ˆ์Œ์„ ์•Œ๋ฆฌ๋Š” ์—ญํ• ์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

interface Observable {
    val observers: ArrayList<Observer>

    fun add(observer: Observer) {
        observers.add(observer)
    }

    fun remove(observer: Observer) {
        observers.remove(observer)
    }

    fun sendUpdateEvent() {
        observers.forEach { it.update() }
    }
}

Blog๊ฐ€ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋˜๋ฉด Follower๋“ค์—๊ฒŒ ๊ทธ ์ •๋ณด(notice)๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ๋ฅผ ๋งŒ๋“ค์–ด๋ดค์Šต๋‹ˆ๋‹ค. ๋จผ์ € Blog ๊ฐ์ฒด์—๋Š” notice ์†์„ฑ์˜ setter๊ฐ€ ๋™์ž‘ํ•˜๊ฒŒ ๋˜๋ฉด sendUpdateEvent ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ฒŒ๋” ํ•ด๋†“์•˜์Šต๋‹ˆ๋‹ค.

class Blog: Observable {

    override val observers: ArrayList<Observer> = ArrayList()
    var notice = ""
        set(value) {
            field = value
            sendUpdateEvent()
        }
}

Blog์˜ Follower๋Š” update๊ฐ€ ์‹คํ–‰๋  ๋•Œ ์ „๋‹ฌ๋ฐ›์€ notice(์ •๋ณด)๋ฅผ ์ฝ˜์†”์— ์ฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

class Follower(private val id: String, private var blog: Blog): Observer {

    override fun update() {
        println("ID: [$id], NOTICE: [${blog.notice}]")
    }
}

์œ„์—์„œ ๊ตฌํ˜„ํ•œ Blog์™€ Follower๋ฅผ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

fun main() {

    val blog = Blog()
    blog.add(Follower("Kotlin", blog))
    blog.add(Follower("Observer", blog))

    blog.notice = "PATTERN"
}

// console
// ID: [Kotlin], NOTICE: [PATTERN]
// ID: [Observer], NOTICE: [PATTERN]

delegates

Delegated๋Š” Kotlin์— ๋‚ด์žฅ๋œ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ๊ทธ์ค‘์—์„œ๋„ Observable ์†์„ฑ(์ด๋ฆ„๋งŒ ๋“ค์–ด๋„ Observer ํŒจํ„ด๊ณผ ๊ด€๋ จ์ด ์žˆ์„ ๊ฒƒ ๊ฐ™์€)์„ ํ™œ์šฉํ•œ ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค.

Delegates์˜ observable์€ ์ฃผ์–ด์ง„ ์†์„ฑ์ด ์ฝ์–ด์ง€๊ฑฐ๋‚˜ ์ˆ˜์ •์ด ์ƒ๊ฒผ์„ ๋•Œ ์‹คํ–‰๋˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝœ๋ฐฑ ํ•จ์ˆ˜์—๋Š” property์™€ oldValue, newValue๊ฐ€ ์ฃผ์–ด์ ธ์„œ ๋‹ค์–‘ํ•œ ์ž‘์—…์ด ๊ฐ€๋Šฅํ•ด ๋ณด์ž…๋‹ˆ๋‹ค. ์˜ˆ์ œ์—์„œ๋Š” newValue๋งŒ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

class Blog {
    val followers = mutableListOf<(String) -> Unit>()

    var notice: String by Delegates.observable("") { property, oldValue, newValue ->
        followers.forEach { it(newValue) }
    }
}

๋žŒ๋‹ค์‹์„ ํ™œ์šฉํ•ด์„œ follwer๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋ชจ์Šต์ž…๋‹ˆ๋‹ค. ๋“ฑ๋ก๋œ follower๋“ค์€ notice๊ฐ€ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚˜๋ฉด ์ฝ˜์†”์— ๊ทธ ์ •๋ณด๋ฅผ ์ฐ์–ด์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

fun main() {
    val blog = Blog()
    blog.followers.add { notice ->
        println("NOTICE: [$notice]")
    }

    blog.notice = "delegates"
}

// console
// NOTICE: [delegates]

์ „์ฒด ์†Œ์Šค ์ฝ”๋“œ๋Š” ์ด๊ณณ์—์„œ ํ™•์ธ ๊ฐ€๋Šฅํ•˜์‹ญ๋‹ˆ๋‹ค.

์ฃผ์ €๋ฆฌ์ฃผ์ €๋ฆฌ

์ œ๊ฐ€ ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋‘ ๋ฒˆ ์ •๋„ ์‚ฌ์šฉํ–ˆ๋˜ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ƒ๋‹นํžˆ ์œ ์šฉํ•˜๋”๋ผ๊ณ ์š”. ๊ทธ๋ฆฌ๊ณ  ์ด๋ฒˆ์— ๊ธ€์„ ์ž‘์„ฑํ•˜๋ฉด์„œ ์•Œ์•„๋ณด๋‹ˆ ๋น„์Šทํ•˜์ง€๋งŒ ๋‹ค๋ฅธ Publisher-Subscriber(๋ฐœํ–‰-๊ตฌ๋…) ํŒจํ„ด์ด ์žˆ๋„ค์š”. ๊ณต๋ถ€๋ฅผ ํ• ์ˆ˜๋ก ๋ชจ๋ฅด๋Š” ๊ฒŒ ๋” ๋งŽ์ด ์ƒ๊ธฐ๋Š” ์ด์ƒํ•œ ์ฝ”๋”ฉ ๊ณต๋ถ€. ์˜ค๋Š˜์€ ์—ฌ๊ธฐ๊นŒ์ง€์ž…๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•

'๐ŸงฉDesign Patterns' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Design Patterns]Factory pattern, ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š”  (0) 2022.07.04

๋Œ“๊ธ€