{"id":336,"date":"2020-05-03T16:40:59","date_gmt":"2020-05-03T07:40:59","guid":{"rendered":"http:\/\/batmask.dothome.co.kr\/?p=336"},"modified":"2020-05-16T01:58:30","modified_gmt":"2020-05-15T16:58:30","slug":"design-pattern-observer","status":"publish","type":"post","link":"http:\/\/batmask.net\/index.php\/2020\/05\/03\/336\/","title":{"rendered":"Design Pattern: Observer"},"content":{"rendered":"\n<p>\ube14\ub85c\uadf8\ub098 \uc720\ud29c\ube0c\ub97c \ubcf4\uba74, \uad6c\ub3c5\uc744 \ud574\ub193\uace0 \uc0c8\uae00\uc774\ub098 \uc601\uc0c1\uc774 \uc62c\ub77c\uc62c \ub54c \uc54c\ub78c\uc744 \ubc1b\ub294\ub2e4. \uc774\ub7f0 \uc0c1\ud638\uc791\uc6a9\uc744 \uac8c\uc2dc-\uad6c\ub3c5(Publish-Subscribe)\uad00\uacc4\ub77c\uace0 \ud55c\ub2e4. Observer pattern\uc740 \uc774\ucc98\ub7fc observer\ub97c \ub4f1\ub85d\ud574\ub193\uace0 \ub300\uc0c1\uc758 \ubcc0\ud654\uc5d0 \ub300\ud55c \uc54c\ub78c\uc744 \ubc1b\ub294 \uacbd\uc6b0\uc5d0 \uc801\uc6a9\ub418\ub294 design pattern\uc774\ub2e4.<\/p>\n\n\n\n<p>Observer pattern\uc740 callback\uc758 \ud615\ud0dc\ub85c \uc774\ubbf8 \ub9ce\uc740 \uacf3\uc5d0\uc11c \uc0ac\uc6a9\ud558\uace0 \uc788\uace0 \uacbd\ud5d8\ud574\ubd24\uc744 \uac83\uc774\ub2e4. \uc548\ub4dc\ub85c\uc774\ub4dc\uc5d0\uc11c BroadcastReceiver\ub97c \ub4f1\ub85d\ud574 \uc0ac\uc6a9\ud558\ub294 \uac83\ub3c4 \uc774\ub7f0 \ud615\ud0dc\uc758 \ud558\ub098\ub77c\uace0 \ubcfc \uc218 \uc788\ub2e4.<\/p>\n\n\n\n<p>\ud558\ub098\uc758 \ub370\uc774\ud130\ub97c \ub450\uace0 \uc5ec\ub7ec\ubc29\uc2dd\uc758 \uadf8\ub798\ud504\ub85c \ud45c\ud604\ud560 \ub54c, \ub370\uc774\ud130\uc758 \ubcc0\ud654\ub97c \uc2e4\uc2dc\uac04 \ubc18\uc601\ud558\ub294 \uacbd\uc6b0\uc5d0\ub3c4 \uc801\uc6a9\ub41c\ub2e4. \uc774 \uacbd\uc6b0, model-view \uad00\uacc4\uac00 \ub418\uace0 \uc870\uae08 \ub354 \ud655\uc7a5\ud574\uc11c Controller\uac00 \ucd94\uac00\ub418\uba74 MVC \ud328\ud134\uc774\ub41c\ub2e4. \uc989, MVC \ud328\ud134\uc5d0\uc11c Model\uacfc View \uc0ac\uc774\uc5d0 Observer Pattern\uc774 \uc0ac\uc6a9\ub41c\ub2e4\ub294 \uc598\uae30.<\/p>\n\n\n\n<p>\ud504\ub85c\uadf8\ub7a8\uc758 Settings \ub610\ub294 Preference \ud56d\ubaa9\uc744 \ubcc0\uacbd\ud558\ub294 \uacbd\uc6b0\ub3c4 \uc801\uc6a9\ud560 \uc218 \uc788\ub2e4. \uc608\ub97c\ub4e4\uc5b4 \ud3f0\ud2b8\ub97c \ubcc0\uacbd\ud558\uba74, \ud604\uc7ac \ubcf4\uc5ec\uc9c0\uace0 \uc788\ub294 \ud14d\uc2a4\ud2b8\uc758 \ud3f0\ud2b8\ub4e4\ub3c4 \ubc14\ub85c \ubcc0\uacbd\ub418\uc5b4\uc57c \ud55c\ub2e4.<\/p>\n\n\n\n<p>\ub2e4\uc790\uac04 \ucc44\ud305 \ud504\ub85c\uadf8\ub7a8\ub3c4 \uc774\ub97c \uc0ac\uc6a9\ud574\uc57c \ud558\ub294\uac78 \uc54c \uc218 \uc788\ub2e4. \ucc44\ud305\ubc29\uc774 subject\uac00 \ub418\uace0 \uc0c8\ub85c\uc6b4 \ucc44\ud305\uc774 \uc62c\ub77c\uc624\uba74 \ubaa8\ub4e0 observer\ub4e4\uc778 \ucc44\ud305 \ucc38\uac00\uc790\ub4e4\uc5d0\uac8c \uc804\ub2ec\ud574\uc57c\ud55c\ub2e4.<\/p>\n\n\n\n<p>UML \ud45c\ud604\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4. ( <a href=\"https:\/\/en.wikipedia.org\/wiki\/Observer_pattern\">Wikipedia \ucc38\uc870<\/a> )<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"240\" src=\"http:\/\/batmask.dothome.co.kr\/wordpress\/wp-content\/uploads\/2020\/05\/W3sDesign_Observer_Design_Pattern_UML.jpg\" alt=\"\" class=\"wp-image-511\" srcset=\"http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2020\/05\/W3sDesign_Observer_Design_Pattern_UML.jpg 600w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2020\/05\/W3sDesign_Observer_Design_Pattern_UML-300x120.jpg 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/figure>\n\n\n\n<p>\ub208\uc5ec\uaca8 \ubd10\uc57c\ud560 \ubd80\ubd84\uc740 update() \ubd80\ubd84\uacfc, update() \uc774\ud6c4, getState()\ub85c \uc0c1\ud0dc\ub97c \uc5bb\uc5b4\uc624\ub294 \ubd80\ubd84\uc774\ub2e4. <br> \uccab\ubc88\uc9f8 \ubb38\uc81c\ub294 \ub3d9\uae30\ud654 \ubb38\uc81c\uc774\ub2e4. \ub2e8\uc77c \uc4f0\ub808\ub4dc\uc77c \ub54c\ub294 \uc0c1\uad00\uc774 \uc5c6\uc9c0\ub9cc, Observer\ub4e4\uc774 \ub3c5\ub9bd\ub41c \ubcc4\uac1c\uc758 \uc4f0\ub808\ub4dc\ub4e4\uc774\ub77c\uba74 \ubb38\uc81c\uac00 \uc0dd\uae34\ub2e4. \uadf8\ub807\ub2e4\uba74 \uc5b4\ub290\ubd80\ubd84\uc5d0 \ub3d9\uae30\ud654 \uc624\ube0c\uc81d\ud2b8\ub098 \ube14\ub7ed\uc744 \uc368\uc57c \ud560\uae4c? \uc0ac\uc2e4, \uc774 \uace0\ubbfc\uc740 Kotlin, C# \uad6c\ud604\uc911\uc5d0\ub294 \ubbf8\ub904\ub480\ub2e4\uac00 python \uad6c\ud604\uc5d0 \uc640\uc11c \ucc3e\uc544\ubd24\ub2e4. \uc815\ub2f5\uc740 Observer \ub9ac\uc2a4\ud2b8\ub97c \ub3d9\uae30\ud654 \uc2dc\ucf1c \uad00\ub9ac\ud574\uc57c \ud558\ub294\uac74\ub370, python \uad6c\ud604\ubd80\ubd84\uc5d0\uc11c \ub2e4\ub8e8\uaca0\ub2e4. \uad81\uae08\ud55c \ubd84\ub4e4\uc744 \uc704\ud574 \uad00\ub828\ud574\uc11c \uc798 \uc815\ub9ac\ub41c \ubb38\uc11c \ub450\uac1c \ub9c1\ud06c\ub97c \ub0a8\uae34\ub2e4. <\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/www.techyourchance.com\/thread-safe-observer-design-pattern-in-java\/\">Thread safe Observer design pattern in Java<\/a><\/li><li><a href=\"https:\/\/python-3-patterns-idioms-test.readthedocs.io\/en\/latest\/Observer.html\">Python3 Patterns, Recipes and Idioms : Observer<\/a><\/li><\/ul>\n\n\n\n<p>\ub458 \uc9f8\ub85c,  observer\uc640 subject\uc758 \ucee4\ud50c\ub9c1 \ubb38\uc81c\uc774\ub2e4. \uadf8\ub798\ud504\uc0c1\uc5d0\uc11c getState()\uac00 concrete class\uc5d0 \uc874\uc7ac\ud55c\ub2e4. \uadf8 \ub9d0\uc740, observer\uac00 concrete class\ub97c \uc54c\uace0 \uc788\uc5b4\uc57c \ud55c\ub2e4\ub294 \ub9d0\uc774\ub2e4. \uc9c0\uae08\uae4c\uc9c0 design pattern\uc744 \uc0b4\ud3b4\ubcf8 \uacbd\ud5d8\uc73c\ub860 getState()\uac00 \uc778\ud130\ud398\uc774\uc2a4\ub098 \ucd94\uc0c1\ud074\ub798\uc2a4\uc5d0 \uc788\uc5b4\uc57c \ud560\uac70 \uac19\ub2e4. \uc2e4\uc81c\ub85c \uadf8\ub807\uac8c \uae30\uc220\ub41c \ubb38\uc11c\ub4e4\ub3c4 \uc788\ub2e4. \uadf8\ub807\uac8c \ub418\uba74 \ub2e4\uc2dc \ubb38\uc81c\uac00 \ub418\ub294 \ubd80\ubd84\uc740 getState()\ub85c \uc5bb\uc5b4\uc62c \ub370\uc774\ud130\uc5d0 \ub300\ud574 \ucd94\uc0c1 \ud074\ub798\uc2a4\uc778 Subject\uac00 \uc54c\uc544\uc57c \ud55c\ub2e4\ub294 \uc810\uc774\ub2e4. <br> \ub610\ud55c, getstate()\uac00 \uc804\uccb4 \ub370\uc774\ud130\ub97c \uac00\uc838\uc624\ub294 \uacbd\uc6b0\uc5d0\ub294 \ubb38\uc81c\uac00 \uc5c6\uaca0\uc9c0\ub9cc, \ubcc0\uacbd\uc0ac\ud56d\ub9cc \uac00\uc838\uc624\ub824\uace0 \ud558\uba74 \uace8\uce58\uac00 \uc544\ud30c\uc9c4\ub2e4. \ud574\uacb0\ucc45\uc73c\ub85c\ub294 \ubcc0\uacbd\uc0ac\ud56d\uc744 \uac00\uc838\uc62c \ub54c, timestamp\ub4f1\uc744 \uc774\uc6a9\ud560 \uc218 \uc788\uaca0\ub2e4. \uc2e4\uc81c \uad6c\ud604\ub4e4\uc744 \ubcf4\uba74 getState()\uac00 \uc544\ub2c8\ub77c update()\uc5d0\uc11c \ubcc0\uacbd\uc0ac\ud56d\uc744 \ub118\uaca8\uc8fc\ub294 \uacbd\uc6b0\ub3c4 \ud754\ud558\ub2e4.<\/p>\n\n\n\n<p> \uc790\ubc14\ub97c \ubcf4\uba74, java.util\uc5d0\uc11c  Observer pattern \uad6c\ud604\uc744 \uc704\ud55c Observable \uacfc Observer\ub97c \uc81c\uacf5\ud558\uace0 \uc788\ub2e4. \ucf54\ub4dc\ub97c \ubcf4\uba74, update(Observable obj, Object arg)\ub85c \uc778\uc790\ub97c \ub118\uae30\uace0 \uc788\ub2e4. \uccab\ubc88\uc9f8 \uc778\uc790\ub85c Observable \uc790\uccb4\ub97c \uc5bb\uc5b4\uc624\uba70, \ub450\ubc88\uc9f8 \uc778\uc790\ub85c \uc0ac\uc6a9\uc790 \uc815\uc758 \ub370\uc774\ud130 \uac1d\uccb4\ub97c \ub118\uaca8\ubc1b\uace0 \uc788\ub2e4. UML\uc5d0\uc11c \ubcf4\uc774\ub4ef\uc774 \uad6c\ud604\uc744 \ud560\uc9c0, \uc790\ubc14\ucc98\ub7fc \uad6c\ud604\uc744 \ud560\uc9c0\ub294 \uc0c1\ud669\uc5d0 \ub9de\uac8c \uc801\uc6a9\ud558\uae38. \uadfc\ub370, \uc77c\uc0c1\uc801\uc73c\ub860 \uc790\ubc14\uc5d0\uc11c\uc758 \uad6c\ud604\uc774 \ubcf4\ud3b8\uc801\uc77c\ub4ef \ud558\ub2e4.<\/p>\n\n\n\n<hr class=\"wp-block-separator is-style-dots\"\/>\n\n\n\n<p>\uc2e4\uc0ac\uc6a9 \uc608\ub4e4\uc774 \ub118\uccd0\ub098\ub294 \ud754\ud558\uac8c \uc4f0\ub294 \ub514\uc790\uc778 \ud328\ud134\uc774\ub77c \ubb34\uc2a8 \uc608\ub97c \ucf54\ub4dc\ub85c \uad6c\ud604\ud574\ubcfc\uae4c \ud558\ub2e4\uac00 \uad70\ub354\ub354\uae30 \uc5c6\ub294 \ucc44\ud305\ubc29\uc744 \ud749\ub0b4\ub0b4 \ubcf4\uae30\ub85c \uacb0\uc815\ud588\ub2e4. \uc2e4\uc81c\ucc98\ub7fc \ub124\ud2b8\uc6cd\uc744 \uc774\uc6a9\ud55c \uad6c\ud604\uc744 \ud560\uae4c \ud558\ub2e4\uac00 \ub2e8\uc9c0 \uc608\ub97c \ubcf4\uc5ec\uc8fc\ub294 \uac83\uc774\ub2c8 \ub85c\uceec\ub85c \ud749\ub0b4\ub9cc \ub0b4\uaca0\ub2e4.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Kotlin<\/h3>\n\n\n\n<p>\uc5ec\uae30\uc11c\ub294 \ub2e4\ub8e8\uc9c0 \uc54a\uaca0\uc9c0\ub9cc, Kotlin\uc5d0\uc120 property\uc5d0 \ub300\ud574 observer pattern\uacfc \uc720\uc0ac\ud55c \ub0b4\uc6a9\uc744 \uc5b8\uc5b4\uc801\uc73c\ub85c \uc9c0\uc6d0\ud558\ub294 <a href=\"https:\/\/kotlinlang.org\/docs\/reference\/delegated-properties.html\">delegate<\/a>\ub77c\ub294\uac8c \uc788\ub2e4. \uc774\ub7f0\uac8c \uc788\ub2e4\ub294 \uac83\ub9cc \uc778\uc9c0\ud558\uace0 \uc77c\ub2e8 \ub118\uc5b4\uac00\uc790.<\/p>\n\n\n\n<p>\uac04\ub2e8\ud558\uac8c \ubcf4\uc5ec\uc8fc\uae30\uc5d0\ub294 java\uc758 \uad6c\ud604 \ud615\ud0dc\uac00 \uc88b\uaca0\uc73c\ub098, \uc870\uae08 \ubcf5\uc7a1\ud558\ub354\ub77c\ub3c4 UML\uc5d0 \uc18c\uac1c\ub41c \ud615\ud0dc\ub85c \uad6c\ud604\ud574 \ubd24\ub2e4. <\/p>\n\n\n\n<pre title=\"ChatRoom.kt\" class=\"wp-block-code\"><code lang=\"kotlin\" class=\"language-kotlin\">package observer\nimport java.time.Instant\n\ndata class SingleChat(val name: String, val chat: String, val timestamp: Instant)\n\nabstract class ChatRoom{\n    private val observers = mutableListOf&lt;IObserver>()\n\n    public fun attach(observer: IObserver){\n        observers.add(observer)\n        observerChanged(observer, true)\n    }\n\n    public fun detach(observer: IObserver){\n        observers.remove(observer)\n        observerChanged(observer, false)\n    }\n\n    protected fun chatNotify(){\n        observers.forEach { observer -> observer.update() }\n    }\n    \n    protected open fun observerChanged(observer: IObserver, attached: Boolean){}\n    abstract fun addChat(data: SingleChat)\n    abstract fun getChat(observer: IObserver): List&lt;SingleChat>\n}\n\nclass MyChatRoom: ChatRoom() {\n    private val chatting = mutableListOf&lt;SingleChat>()\n    private val timeline = mutableMapOf&lt;IObserver, Instant>()\n\n    override fun observerChanged(observer: IObserver, attached: Boolean) {\n        if(!attached){\n            timeline.remove(observer)\n        }\n    }\n\n    public override fun addChat(data: SingleChat){\n        chatting.add(data)\n        chatNotify()\n    }\n\n    public override fun getChat(observer: IObserver): List&lt;SingleChat>{\n        val timestamp = timeline.get(observer)\n        timeline[observer] = Instant.now()\n\n        return if(timestamp != null){\n            chatting.filter{it.timestamp > timestamp}\n        }else\n            chatting\n    }\n}<\/code><\/pre>\n\n\n\n<p>\uc6b0\uc120 \ucc44\ud305\uc744 \uc800\uc7a5\ud558\uace0 Observer\uc5d0\uac8c \uc804\ub2ec\ud560 \ucc44\ud305 \uae30\ubcf8 \ud3ec\ub9f7\uc744 data class\ub85c \ub9cc\ub4e4\uc5c8\ub2e4. \uc774\ub984, \ub0b4\uc6a9, \ud0c0\uc784\uc2a4\ud0ec\ud504\ub85c \uc774\ub8e8\uc5b4\uc838 \uc788\ub2e4.<br> \uad6c\ud604 \ubd80\ubd84\uc740 \uc774\uac8c \ucd5c\uc120\uc778\uac00 \uc2f6\uae34 \ud55c\ub370, \ubd80\ubaa8 \ud074\ub798\uc2a4\uc5d0 observer\uc640 \uad00\ub828\ub41c \ucf54\ub4dc\ub9cc \ube7c\ub0b4\uc9c0 \uc54a\uace0, addChat, getChat \uc778\ud130\ud398\uc774\uc2a4\ub97c \ucd94\uac00\ud588\ub2e4. \uacb0\uad6d, \uc5b4\ub5a4 \ub370\uc774\ud130\ub97c \uc8fc\uace0\ubc1b\uc744\uc9c0 \uc54c\uace0 \uc788\uc5b4\uc57c \ud558\ub294\ub370 Observer\ucabd\uc5d0 Concrete class\uac00 \uc544\ub2cc \uc778\ud130\ud398\uc774\uc2a4\ub9cc \ub4dc\ub7ec\ub0b4\uace0 \uc2f6\uc5c8\ub2e4. observer\uad00\ub828 \ucf54\ub4dc\ub9cc \uc644\ubcbd\ud558\uac8c \ubd80\ubaa8\ud074\ub798\uc2a4\ub85c \ube7c\uace0 \uc2f6\ub2e4\uba74, java\uc758 Observable\uc744 \ucc38\uace0\ud558\uba74 \ub41c\ub2e4.<\/p>\n\n\n\n<p> concrete class\uc778 MyChatRoom\uc744 \ubcf4\uba74, addChat()\uc73c\ub85c \ucc44\ud305\uc774 \ucd94\uac00\ub418\uba74, chatNotify()\ub85c observer\ub4e4\uc5d0\uac8c \ubcc0\uacbd\ub418\uc5c8\uc74c\uc744 \ube0c\ub85c\ub4dc\uce90\uc2a4\ud305\ud558\uac8c \ub41c\ub2e4. \uc774\ub97c \ubc1b\uc740 observer \ub4e4\uc740 getChat()\uc778\ud130\ud398\uc774\uc2a4\ub97c \ud1b5\ud574 \uc0c8\ub85c\uc6b4 \ucc44\ud305\uc744 \ubc1b\uc544\uac00\ub294\ub370, \uc0c8\ub85c\uc6b4 \ub0b4\uc6a9\ub9cc \uac00\uc838\uac00\ub3c4\ub85d timestamp\ub97c \uc774\uc6a9\ud588\ub2e4. \uc774\uc804\uc5d0 getChat()\uc744 \ud1b5\ud574 \ub300\ud654\ub0b4\uc6a9\uc744 \uac00\uc838\uac14\uc5c8\ub2e4\uba74, \uadf8 timestamp\ub97c \uae30\ub85d\ud574\ub193\uace0 \uadf8 \uc774\ud6c4\uc758 \ub300\ud654\ub0b4\uc6a9\ub9cc \ub9ac\uc2a4\ud2b8 \ud615\ud0dc\ub85c \ub3cc\ub824\uc900\ub2e4. \ub9cc\uc57d \ucc98\uc74c \ubd80\ub974\ub294 \uac70\ub77c\uba74, timestamp\uae30\ub85d\uc774 \uc5c6\uc73c\ubbc0\ub85c \uc804\uccb4 \ub300\ud654\ub0b4\uc6a9\uc744 \ub3cc\ub824\uc900\ub2e4. <br> \uc774\ubd80\ubd84 \ub54c\ubb38\uc5d0 \uc608\uc0c1\uc5d0 \uc5c6\uc5c8\ub358 \ucf54\ub4dc\ub97c \ucd94\uac00\ud588\ub294\ub370, observerChanged()\ub77c\ub294 \uba54\uc18c\ub4dc\ub97c \ub9cc\ub4e4\uace0 attach(), detach()\ub420 \ub54c\ub9c8\ub2e4 \ud638\ucd9c\ub418\ub3c4\ub85d \ud588\ub2e4. detach \ub420 \ub54c, timestamp \uae30\ub85d\uc5d0\uc11c\ub3c4 \uc81c\uac70\ud558\uae30 \uc704\ud574\uc11c\uc774\ub2e4. \uae30\ubcf8 \ucf54\ub4dc\ub97c \uc720\uc9c0\ud558\uace0 \uc2f6\uc5b4 \uc774\ub807\uac8c \ucd94\uac00\ub97c \ud588\ub294\ub370, \uc774\uac8c \ucd5c\uc120\uc778\uc9c0\ub294 \ubaa8\ub974\uaca0\ub2e4. <\/p>\n\n\n\n<pre title=\"ChatClient\" class=\"wp-block-code\"><code lang=\"kotlin\" class=\"language-kotlin\">package observer\nimport java.time.Instant\n\ninterface IObserver{\n    fun update()\n}\n\nclass ChatClient: IObserver{\n    private var chatName: String = \"\"\n    private var chatRoom: ChatRoom? = null\n\n    fun connect(name: String, room: ChatRoom){\n        chatName = name\n        chatRoom = room\n        room.attach(this)\n    }\n\n    fun disconnect(){\n        chatRoom?.detach(this)\n        chatRoom = null\n    }\n\n    fun talk(chat: String){\n        chatRoom?.addChat(SingleChat(chatName, chat, Instant.now()))\n    }\n\n    override fun update() {\n        if(chatRoom != null){\n            val chatting = chatRoom!!.getChat(chatName)\n            chatting.forEach{println(\"[client: $chatName] [${it.timestamp}] ${it.name}: ${it.chat}\")}\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>Observer \uc778\ud130\ud398\uc774\uc2a4\uc640 \uac00\uc0c1 \ucc44\ud305 \ud074\ub77c\uc774\uc5b8\ud2b8  \ucf54\ub4dc\uc774\ub2e4. connect() \uc640 disconnect()\uc5d0\uc11c observer\ub97c \ub4f1\ub85d\ud574\uc8fc\uace0, update()\uac00 \ud638\ucd9c\ub418\uba74 getChat\uc744 \uc774\uc6a9\ud574 \ub300\ud654 \ub0b4\uc6a9\uc744 \ubc1b\uc544\uc640 \ucd9c\ub825\ud55c\ub2e4. \ub300\ud654\ub97c \uc62c\ub9b4 \ub550 talk() \uba54\uc18c\ub4dc\ub97c \uc774\uc6a9\ud55c\ub2e4. \uc5ec\uae30\uc5d0\uc11c timestamp\uac00 \ucc0d\ud78c \ub300\ud654\uac00 \uc62c\ub77c\uac00\uac8c\ub41c\ub2e4.<\/p>\n\n\n\n<pre title=\"Main.kt\" class=\"wp-block-code\"><code lang=\"kotlin\" class=\"language-kotlin\">package observer\n\nfun main(args: Array&lt;String>){\n    val room: ChatRoom = MyChatRoom()\n    val client1: ChatClient = ChatClient()\n    client1.connect(\"bato\", room)\n\n    val client2: ChatClient = ChatClient()\n    client2.connect(\"wan-tae\", room)\n\n    val client3: ChatClient = ChatClient()\n    client3.connect(\"jung-a\", room)\n\n    client1.talk(\"hi! I'm bato\")\n    Thread.sleep(100)\n    client3.talk(\"hi! I'm jung-a\")\n    Thread.sleep(100)\n    client2.talk(\"Do you see my chat?\")\n    Thread.sleep(100)\n    client1.disconnect()\n    client3.disconnect()\n    client2.talk(\"Hello?\")\n}<\/code><\/pre>\n\n\n\n<pre title=\"result\" class=\"wp-block-code\"><code lang=\"rest\" class=\"language-rest\">[client: bato] [2020-05-02T06:59:44.817809200Z] bato: hi! I'm bato\n[client: wan-tae] [2020-05-02T06:59:44.817809200Z] bato: hi! I'm bato\n[client: jung-a] [2020-05-02T06:59:44.817809200Z] bato: hi! I'm bato\n[client: bato] [2020-05-02T06:59:44.930179600Z] jung-a: hi! I'm jung-a\n[client: wan-tae] [2020-05-02T06:59:44.930179600Z] jung-a: hi! I'm jung-a\n[client: jung-a] [2020-05-02T06:59:44.930179600Z] jung-a: hi! I'm jung-a\n[client: bato] [2020-05-02T06:59:45.030512600Z] wan-tae: Do you see my chat?\n[client: wan-tae] [2020-05-02T06:59:45.030512600Z] wan-tae: Do you see my chat?\n[client: jung-a] [2020-05-02T06:59:45.030512600Z] wan-tae: Do you see my chat?\n[client: wan-tae] [2020-05-02T06:59:45.130844600Z] wan-tae: Hello?\n\nProcess finished with exit code 0\n<\/code><\/pre>\n\n\n\n<p>\ud14c\uc2a4\ud2b8 \ucf54\ub4dc\ub294 \uc704\uc640 \uac19\ub2e4. \ud14c\uc2a4\ud2b8 \ub300\ud654 \uc911\uac04\uc5d0 sleep()\uc744 \ub123\uc5c8\ub294\ub370, \uc774\uac8c \uc5c6\uc73c\uba74 \uc2e4\ud589\uc18d\ub3c4\uac00 \ube68\ub77c timestamp\uac00 \uc81c\ub300\ub85c \uc791\ub3d9\ud558\uc9c0 \uc54a\ub294\ub2e4. \uc2e4\ud589 \uacb0\uacfc\ub97c \ubcf4\uba74, \ud55c\uba85\uc774 \ub9d0\ud560 \ub54c \uac01 \ud074\ub77c\uc774\uc5b8\ud2b8\uc5d0\uc11c \ub300\ud654\ub97c \ubc1b\uac8c\ub418\uba70 \ub9c8\uc9c0\ub9c9 \ub300\ud654\ub294 \ub450 \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 disconnect\ub41c \uc0c1\ud0dc\ub77c\uc11c \uc804\ub2ec\ub418\uc9c0 \uc54a\ub294\uac78 \ubcfc \uc218 \uc788\ub2e4.<\/p>\n\n\n\n<hr class=\"wp-block-separator is-style-dots\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">C#<\/h3>\n\n\n\n<p>C#\uc5d0\uc11c\ub294 \uc870\uae08 \ub2e4\ub978\uac78 \uad6c\ud604\ud574 \ubcf4\ub824 \ud55c\ub2e4. \uc55e\uc11c, Kotlin\uc758 delegate\ub97c \uc7a0\uae50 \uc5b8\uae09\ud588\uc9c0\ub9cc, C#\uc5d0\uc11c\ub294 event, delegate \ub77c\ub294\uac78 \uc9c0\uc6d0\ud574\uc11c \uc774\uac78 \uc774\uc6a9\ud560 \uc218\ub3c4 \uc788\ub2e4. \ub610\ud55c, JAVA\uc640 \uc720\uc0ac\ud558\uac8c IObserver&lt;T&gt;, IObservable&lt;T&gt; \uc778\ud130\ud398\uc774\uc2a4\ub97c \uc81c\uacf5\ud55c\ub2e4.( <a href=\"https:\/\/docs.microsoft.com\/ko-kr\/dotnet\/standard\/events\/observer-design-pattern\">MS \uacf5\uc2dd \ubb38\uc11c \ucc38\uc870<\/a> ) \uad6c\ud604\uc744 \uc0b4\ud3b4\ubcf4\ub2e4\ubcf4\ub2c8, \uc0dd\uac01\ubcf4\ub2e4 \ub0af\uc120 \ub0b4\uc6a9\uc774\ub77c\uc11c \uc774\uac78 \uc774\uc6a9\ud574 preference\ub97c \uad6c\ud604\ud574 \ubcf4\uaca0\ub2e4.<\/p>\n\n\n\n<pre title=\"Preference.cs\" class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\nnamespace Observer\n{\n    public struct ObserverData\n    {\n        public int volume;\n        public Color color1;\n        public Color color2;\n\n        public ObserverData(int vol, Color color1, Color color2)\n        {\n            this.volume = vol;\n            this.color1 = color1;\n            this.color2 = color2;\n        }\n\n    }\n\n    public class GamePreference: IObservable&lt;ObserverData>\n    {\n        private static readonly Lazy&lt;GamePreference> lazy = new Lazy&lt;GamePreference>(() => new GamePreference());\n\n        private ObserverData _prefData;\n        private List&lt;IObserver&lt;ObserverData>> observers;\n\n        public ObserverData PrefData\n        {\n            get => _prefData;\n            set\n            {\n                _prefData = value;\n                PrefChangeNotify(_prefData);\n            }\n        }\n\n        private GamePreference()\n        {\n            _prefData = new ObserverData(0, Color.white, Color.white);\n            observers = new List&lt;IObserver&lt;ObserverData>>();\n        }\n\n        public static GamePreference Instance { get { return lazy.Value; } }\n\n        public IDisposable Subscribe(IObserver&lt;ObserverData> observer)\n        {\n            if (!observers.Contains(observer)) observers.Add(observer);\n            return new Unsubscriber(observers, observer);\n        }\n\n        public void PrefChangeNotify(ObserverData data)\n        {\n            foreach(IObserver&lt;ObserverData> observer in observers)\n            {\n                observer.OnNext(data);\n            }\n        }\n\n        private class Unsubscriber: IDisposable\n        {\n            private List&lt;IObserver&lt;ObserverData>> _observers;\n            private IObserver&lt;ObserverData> _observer;\n\n            public Unsubscriber(List&lt;IObserver&lt;ObserverData>> observers, IObserver&lt;ObserverData> observer)\n            {\n                this._observers = observers;\n                this._observer = observer;\n            }\n\n            public void Dispose()\n            {\n                if (_observer != null) _observers.Remove(_observer);\n            }\n        }\n    }\n\n}\n<\/code><\/pre>\n\n\n\n<p>Observer\uac00 \ubc1b\uac8c\ub420 struct\ub97c \uba3c\uc800 \uc815\uc758\ud588\ub2e4. \uac00\uc0c1\uc758 preference\uc774\ubbc0\ub85c volume \uacfc color\uac12\uc744 \uac00\uc815\ud588\ub2e4. <\/p>\n\n\n\n<p>Preference\ub294 \uac8c\uc784\ub0b4\uc5d0\uc11c \ub531 \ud558\ub098\ub9cc \uc874\uc7ac\ud574\uc57c\ud55c\ub2e4. \uadf8\ub798\uc11c singleton \uad6c\ud604\ucf54\ub4dc\ub97c \ucd94\uac00\ud588\ub2e4. ( <a href=\"http:\/\/batmask.dothome.co.kr\/index.php\/2020\/04\/05\/270\/\">\uc774\uc804\uc5d0 \uc62c\ub9b0 \ud3ec\uc2a4\ud305\uc744 \ucc38\uc870<\/a> )<\/p>\n\n\n\n<p>\ud575\uc2ec\uc740 IObservable&lt;ObserverData&gt; \uc778\ud130\ud398\uc774\uc2a4\uc758 \uad6c\ud604\uc774\ub2e4. Subscribe()\ub97c \uad6c\ud604\ud588\ub294\ub370 \ub9ac\ud134\uac12\uc774 IDisposable\uc774\ub2e4.<a href=\"https:\/\/docs.microsoft.com\/ko-kr\/dotnet\/standard\/events\/how-to-implement-a-provider\"> MS \ubb38\uc11c\ub97c \ucc38\uc870<\/a>\ud574\uc11c Unsubscriber\ub97c \uad6c\ud604\ud588\ub2e4. observer pattern\uc5d0\uc11c\ub294 Observer\uce21\uc5d0\uc11c detach\ud558\uae30\uc704\ud574, Subject(\ub610\ub294 Provider)\ub97c \ub4e4\uace0 \uc788\uc5b4\uc57c \ud558\ub294\ub370, \uc5ec\uae30\uc11c\ub294 \uc774\ubd80\ubd84\uc744 IDisposable \uc778\ud130\ud398\uc774\uc2a4\ub85c \ub9e4\uc6b0 \uae54\ub054\ud558\uac8c \uad6c\ud604\ud558\uc600\ub2e4. \ubb3c\ub860, \uc5ec\uae30\uc11c\ub294 provider\uac00 singleton\uc774\uae30 \ub54c\ubb38\uc5d0 \uad73\uc774 \uc774\ub807\uac8c \uc548\ud574\ub3c4 \ub418\uaca0\uc9c0\ub9cc, \uc77c\ubc18\uc801\uc778 Observer Pattern\uc5d0\uc11c\ub294 \uac00\uc7a5 \uae54\uae08\ud55c \ucc98\ub9ac\ubc29\ubc95\uc774 \uc544\ub2cc\uac00 \uc0dd\uac01\ub41c\ub2e4.<\/p>\n\n\n\n<pre title=\"PrefObserver.cs\" class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\nnamespace Observer\n{\n    public class PrefObserver : MonoBehaviour, IObserver&lt;ObserverData>\n    {\n        private IDisposable unsubscriber;\n\n        public virtual void Subscribe(IObservable&lt;ObserverData> provider)\n        {\n            unsubscriber = provider.Subscribe(this);\n        }\n\n        public virtual void Unsubscribe()\n        {\n            unsubscriber.Dispose();\n        }\n\n        public void OnCompleted()\n        {\n            \/\/ Not used here\n            Debug.Log(\"Observable data transfer complete.\");\n        }\n\n        public void OnError(Exception error)\n        {\n            \/\/ Not used here\n            Debug.Log(\"Observable data transfer error.\");\n        }\n\n        public void OnNext(ObserverData data)\n        {\n            Debug.Log(gameObject.name + \":\" + data.volume + \", \" + data.color1 + \", \" + data.color2);\n        }\n\n        private void OnEnable()\n        {\n            GamePreference pref = GamePreference.Instance;\n            unsubscriber = pref.Subscribe(this);\n        }\n        private void OnDisable()\n        {\n            if (unsubscriber != null) unsubscriber.Dispose();\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>Observer\uce21\uc5d0\uc120 IObserver&lt;ObserverData&gt; \uc758 \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud574\uc57c \ud55c\ub2e4. \ubcc0\uacbd\uc0ac\ud56d\uc740 OnNext()\uac00 \ud638\ucd9c\ub418\uace0, \ub354\uc774\uc0c1 \ubcc0\ud654\uac00 \uc5c6\uc744 \uacbd\uc6b0 OnComplete()\uc774 \ud638\ucd9c\ub41c\ub2e4. \uc5d0\ub7ec\uac00 \ubc1c\uc0dd\ud558\uba74 OnError()\ub97c \uc4f4\ub2e4. \uc774\ub807\uac8c 3\uac1c\uc758 \uc778\ud130\ud398\uc774\uc2a4\uc778\ub370, \uc0ac\uc2e4 Observable\uc5d0\uc11c \uc5b4\ub5bb\uac8c \ud638\ucd9c\ud558\ub0d0\uc5d0 \ub2ec\ub824\uc788\ub2e4. \uc77c\ubc18\uc801\uc778 update()\ub9cc \uc0dd\uac01\ud558\uba74 OnNext()\ub9cc \uad6c\ud604\ud558\uba74 \ub418\uace0 \uc5ec\uae30\uc11c \uadf8\ub807\uac8c \ud588\ub2e4. <\/p>\n\n\n\n<p>\uace0\ubbfc\ub418\ub358 \uc9c0\uc810\uc740 \ubc1b\uc544\uc624\ub294 ObserverData \uc778\ub370, \ud558\ub098\uc758 \ub370\uc774\ud130 \uac1d\uccb4\ub85c preference\uc758 \ubcc0\uacbd\ub41c \ubd80\ubd84\ub9cc \uc804\ub2ec\ud558\uae30\uac00 \uc880 \uc560\ub9e4\ud574\uc11c \ud1b5\ucc44\ub85c \uc804\ub2ec\ud588\ub2e4. \uc880 \ub354 \ubc1c\uc804\uc2dc\ud0a8\ub2e4\uba74, Dictionary\ub97c \uc4f0\ub294 \ubc29\ubc95\uc774 \uc788\uc744 \uc218 \uc788\uaca0\ub2e4.<\/p>\n\n\n\n<p>Observer\uc758 \ub4f1\ub85d\uacfc \ud574\uc9c0\ub294 OnEnable(), OnDisable()\uc5d0\uc11c \uad6c\ud604\ud588\ub2e4. Subscribe()\uc5d0\uc11c IDisposable \uad6c\ud604\uc744 \ubc1b\uc544\uc640 \uc800\uc7a5\ud588\ub2e4\uac00, \ud574\uc9c0\ud560 \ub54c Dispose()\ub9cc \ud638\ucd9c\ud574\uc8fc\uba74 \ub41c\ub2e4.<\/p>\n\n\n\n<p>Unity\uc5d0\uc11c \uad6c\ud604\ub41c \uac83\uc73c\ub85c \uc5b4\ub290 \uac1d\uccb4\uc5d0\ub4e0 script component\ub85c \ubd99\uc5ec\ub193\uc73c\uba74 \ub41c\ub2e4. Preference\ub294 singleton\uc73c\ub85c \ub2e8\uc77c\ud558\uc9c0\ub9cc, observer\uac00 \ub418\ub294 \uac1d\uccb4\ub294 \uc5bc\ub9c8\ub4e0\uc9c0 \uc788\uc744 \uc218 \uc788\ub2e4.<\/p>\n\n\n\n<pre title=\"PrefTestScript.cs\" class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\nnamespace Observer\n{\n    public class PrefTestScript : MonoBehaviour\n    {\n        private List&lt;ObserverData> sample = new List&lt;ObserverData>();\n        private int index = 0;\n        \/\/ Start is called before the first frame update\n        void Start()\n        {\n            sample.Add(new ObserverData(30, Color.green, Color.cyan));\n            sample.Add(new ObserverData(100, Color.gray, Color.magenta));\n            sample.Add(new ObserverData(10, Color.yellow, Color.magenta));\n        }\n\n        \/\/ Update is called once per frame\n        void Update()\n        {\n            if (Input.GetKeyDown(KeyCode.Space))\n            {\n                if(index &lt; sample.Count)\n                {\n                    GamePreference.Instance.PrefData = sample[index];\n                    index++;\n                }\n            }\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>Preference\ub97c \uc704\ud55c UI\ub97c \ub9cc\ub4dc\ub294\uac74 \uc2dc\uac04\uc774 \uac78\ub9ac\ubbc0\ub85c \uc21c\uc804\ud788 \ud14c\uc2a4\ud2b8\ub97c \uc704\ud574 \ub9cc\ub4e0 \ucf54\ub4dc\uc774\ub2e4. Preference\ub97c \ubcc0\uacbd\ud558\uba74\uc11c Observer\ub4e4\uc774 \ub370\uc774\ud130\ub97c \ubc1b\ub294\uc9c0 \ud655\uc778\ud574\uc57c \ud558\ub294\ub370, \uadf8 \ubcc0\uacbd\uc744 \uc5ec\uae30\uc5d0\uc11c \ud55c\ub2e4. \ud14c\uc2a4\ud2b8\uc6a9 \uac1d\uccb4\ub97c \ud558\ub098 \ub9cc\ub4e4\uace0 \uc774 \uc2a4\ud06c\ub9bd\ud2b8 \ucef4\ud3ec\ub10c\ud2b8\ub97c \ucd94\uac00\ud55c \ud6c4 \uc2e4\ud589\ud558\uba74 \uac8c\uc784\ud654\uba74\uc5d0\uc11c \uc2a4\ud398\uc774\uc2a4\ubc14\uac00 \ub20c\ub9b4 \ub54c\ub9c8\ub2e4 \uc0d8\ud50c \ub370\uc774\ud130\uac00 \uc785\ub825\ub41c\ub2e4. \uacb0\uacfc\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"577\" height=\"242\" src=\"http:\/\/batmask.dothome.co.kr\/wordpress\/wp-content\/uploads\/2020\/05\/observer_pattern_result.png\" alt=\"\" class=\"wp-image-524\" srcset=\"http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2020\/05\/observer_pattern_result.png 577w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2020\/05\/observer_pattern_result-300x126.png 300w\" sizes=\"auto, (max-width: 577px) 100vw, 577px\" \/><\/figure><\/div>\n\n\n\n<p>PrefObserver1, PrefObserver2 \ub450\uac1c\uc758 Observer\uac1d\uccb4\ub97c \ub9cc\ub4e4\uace0 \ud14c\uc2a4\ud2b8\ub97c \ub3cc\ub824\ubd24\ub2e4. \uc2a4\ud398\uc774\uc2a4\ubc14\uac00 \ub20c\ub9b4 \ub54c\ub9c8\ub2e4 \uac01\uac01 \uac12\uc744 \uc798 \ubc1b\uc544\uac00\ub294\uac78 \ubcfc \uc218 \uc788\ub2e4.<\/p>\n\n\n\n<p>\ucc98\uc74c\uc5d4 event\uc640 delegate\ub97c \uc774\uc6a9\ud55c \uac04\ub2e8\ud55c\uac78 \ub9cc\ub4e4 \uc0dd\uac01\uc774\uc5c8\ub294\ub370, Observer\uad00\ub828 \uc778\ud130\ud398\uc774\uc2a4\uc640 \uc124\uba85 \ubb38\uc11c\uac00 \ub531 \ub098\uc640\uc788\uc73c\ub2c8 \ub9cc\ub4e4\uc5b4 \ubd10\uc57c\ud588\ub2e4. Observer pattern\uc740 \ub0a0\ub85c\uba39\uc744 \uc0dd\uac01\uc744 \ud558\uace0 \uc788\uc5c8\ub294\ub370, \uc0c8\ub85c\uc6b4 \ub0b4\uc6a9\ub3c4 \ub9ce\uace0 \ubcf5\uc7a1\ud55c\uac70 \uac19\ub2e4.<\/p>\n\n\n\n<hr class=\"wp-block-separator is-style-dots\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Python<\/h3>\n\n\n\n<p>Kotlin\uc73c\ub85c \uad6c\ud604\ud588\ub358 \ucc44\ud305\ubc29 \ud749\ub0b4\ub97c \ub2e4\uc2dc \ub0b4\ubcf4\uc790. \uc774\ubc88\uc5d0\ub294 thread safe \uae4c\uc9c0 \uace0\ub824\ud574 \ub3d9\uae30\ud654 \ucf54\ub4dc\ub97c \ucd94\uac00\ud560 \uac83\uc774\ub2e4. <\/p>\n\n\n\n<p>python\uc5d0\uc11c\ub294 java\ucc98\ub7fc synchronized \uac19\uc740\uac78 \uc9c0\uc6d0\ud558\uc9c0 \uc54a\ub294\ub2e4. \ub300\uc2e0 Lock\uacfc \uac19\uc740 \ub3d9\uae30\ud654 \uc624\ube0c\uc81d\ud2b8\ub4e4\uc740 \uc9c0\uc6d0\ud55c\ub2e4. ( <a href=\"https:\/\/docs.python.org\/3\/library\/asyncio-sync.html\">\uacf5\uc2dd \ubb38\uc11c \ucc38\uc870<\/a> ) <\/p>\n\n\n\n<pre title=\"data.py\" class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">from dataclasses import dataclass\n\n@dataclass\nclass SingleChat:\n    name: str = \"\"\n    chat: str = \"\"\n    timestamp: float = 0.\n<\/code><\/pre>\n\n\n\n<p>Kotlin\uc5d0\uc11c\uc640 \ub3d9\uc77c\ud558\uac8c  \ucc44\ud305 \ub370\uc774\ud130 \ud074\ub798\uc2a4\ub97c \uc815\uc758\ud588\ub2e4. python\uc5d0\uc11c timestamp\ub294 float\ub85c \ud45c\uc2dc\ub418\ub294\ub370, \uc18c\uc218\uc810 \uc704\ub294 \ucd08\ub2e8\uc704, \uc544\ub798\ub294 \ubc00\ub9ac\ucd08 \ub2e8\uc704\ub97c \ud45c\uc2dc\ud55c\ub2e4.<\/p>\n\n\n\n<pre title=\"abc_observer.py\" class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">from abc import ABC, abstractmethod\nfrom typing import List\nimport threading\nimport copy\n\n\nclass IObserver(ABC):\n    @abstractmethod\n    def update(self, data):\n        pass\n\nclass Observable:\n    def __init__(self):\n        self.observers: List[IObserver] = []\n        self.lock = threading.Lock()\n\n    def attach(self, observer: IObserver):\n        with self.lock:\n            self.observers.append(observer)\n\n    def detach(self, observer: IObserver):\n        with self.lock:\n            self.observers.remove(observer)\n\n    def chat_notify(self, data):\n        with self.lock:\n            observers_copy = self.observers[:]\n\n        for observer in observers_copy:\n            observer.update(data)\n<\/code><\/pre>\n\n\n\n<p>Java \ucc98\ub7fc IObserver\uc640 Observable \ud074\ub798\uc2a4\ub97c \ub9cc\ub4e4\uc5c8\ub2e4. dynamic type \uc5b8\uc5b4\uc758 \uc7a5\uc810\uc774 \ubcf4\uc774\ub294\ub370, notify\ub85c \uc804\ub2ec\ud558\ub294 data\uc758 \ud0c0\uc785\uc744 \uc9c0\uc815\ud558\uc9c0 \uc54a\uc544\ub3c4 \ub418\uc11c concrete class\uc640 \uc644\uc804\ud788 \ubd84\ub9ac\ub41c \uac78 \ubcfc \uc218 \uc788\ub2e4.<\/p>\n\n\n\n<p>\uc8fc\ubaa9\ud560 \uc810\uc740 thread-safe \ucf54\ub4dc\uc758 \ucd94\uac00\uc778\ub370, lock\uc744 \ud558\ub098 \uc0dd\uc131\ud558\uace0 \uc5ec\ub7ec \uc4f0\ub808\ub4dc\uc5d0\uc11c \uc811\uadfc\ud558\ub294 \ub370\uc774\ud130\uc778 observers list \uc0ac\uc6a9\uc2dc, lock\uc744 \uac78\uace0 \uc0ac\uc6a9\ud574 \ub3d9\uae30\ud654 \uc2dc\ucf30\ub2e4. notify\ud558\ub294 \ubd80\ubd84\uc5d0\uc11c\ub294 lock\uc774 \uac78\ub9b0\ucc44\ub85c observer.update()\ub97c \ud638\ucd9c\ud558\uba74 deadlock\uc744 \uc720\ubc1c\ud560 \uc218 \uc788\uc73c\ubbc0\ub85c, \ub9ac\uc2a4\ud2b8\ub97c \ubcf5\uc0ac\ud560 \ub54c\ub9cc lock\uc744 \uac78\uace0 \ubcf5\uc0ac\ub41c \ub370\uc774\ud130\ub97c \uc0ac\uc6a9\ud588\ub2e4.<\/p>\n\n\n\n<pre title=\"chatroom.py\" class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">from observer.data import SingleChat\nfrom observer.abc_observer import Observable\n\nclass ChatRoom(Observable):\n    def talk(self, data: SingleChat):\n        self.chat_notify(data)\n<\/code><\/pre>\n\n\n\n<p>Observable\uc744 \uc0c1\uc18d\ud55c \ucc44\ud305\ubc29 \ud074\ub798\uc2a4\uc774\ub2e4. \uc55e\uc11c Kotlin\uc758 \uad6c\ud604\uacfc \ub2e4\ub974\uac8c, notify\uc5d0\uc11c \ub370\uc774\ud130\uac00 \uc804\ub2ec\ub418\ubbc0\ub85c \uc0c8\ub85c\uc6b4 \ub300\ud654\ub97c \ucd94\uac00\ud558\ub294 talk\ub9cc \ucd94\uac00 \ub418\uc5c8\ub2e4. Observer\uad00\ub828 \ucf54\ub4dc\ub3c4 \uae54\ub054\ud558\uac8c \ub5a8\uc5b4\uc838\uc11c \ub2e4\ub978 \ucd94\uac00\ucf54\ub4dc\ub294 \ud544\uc694\uce58 \uc54a\uc558\ub2e4.<\/p>\n\n\n\n<pre title=\"chatclient.py\" class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">from abc import ABC, abstractmethod\nfrom observer.chatroom import ChatRoom\nfrom observer.data import SingleChat\nfrom datetime import datetime\nimport time\nfrom observer.abc_observer import IObserver\n\nclass ChatClient(IObserver):\n    def __init__(self):\n        self.name: str = None\n        self.room: ChatRoom = None\n\n    def update(self, data: SingleChat):\n        print(f\"[{self.name}] {data.name}, {data.chat} - {datetime.fromtimestamp(data.timestamp)}\")\n\n    def connect(self, name: str, room: ChatRoom):\n        self.name = name\n        self.room = room\n        self.room.attach(self)\n\n    def disconnect(self):\n        self.room.detach(self)\n        self.room = None\n\n    def talk(self, msg: str):\n        if self.room is not None:\n            self.room.talk(SingleChat(self.name, msg, time.time()))\n<\/code><\/pre>\n\n\n\n<p>Observer\uac00 \ub418\ub294 \ucc44\ud305 \ud074\ub77c\uc774\uc5b8\ud2b8 \uad6c\ud604\uc774\ub2e4. IObserver\ub97c \uc0c1\uc18d\ud574\uc11c update()\ub97c override\ud588\ub2e4. time.time()\uc740 \ud604\uc7ac\uc758 timestamp\ub97c \uac00\uc838\uc624\ub294 \ud568\uc218\uc774\ub2e4.<\/p>\n\n\n\n<pre title=\"main.py\" class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">import threading\n\nfrom observer.chatroom import ChatRoom\nfrom observer.chatclient import ChatClient\nimport time\n\ndef client1_thread(client: ChatClient):\n    time.sleep(0.1)\n    client.talk(\"hi! I'm bato\")\n    time.sleep(1)\n    client.disconnect()\n\ndef client2_thread(client: ChatClient):\n    time.sleep(0.5)\n    client.talk(\"Do you see my chat?\")\n    time.sleep(2)\n    client.talk(\"Hello?\")\n\n\ndef client3_thread(client: ChatClient):\n    time.sleep(0.7)\n    client.talk(\"hi! I'm jung-a\")\n    time.sleep(0.1)\n    client.disconnect()\n\ndef main():\n    room1 = ChatRoom()\n    client1 = ChatClient()\n    client1.connect(\"bato\", room1)\n    client2 = ChatClient()\n    client2.connect(\"wan-tae\", room1)\n    client3 = ChatClient()\n    client3.connect(\"jung-a\", room1)\n\n    thread1 = threading.Thread(target=client1_thread, args=(client1,))\n    thread2 = threading.Thread(target=client2_thread, args=(client2,))\n    thread3 = threading.Thread(target=client3_thread, args=(client3,))\n\n    thread1.start()\n    thread2.start()\n    thread3.start()\n\n\nif __name__ == \"__main__\":\n    main()\n<\/code><\/pre>\n\n\n\n<p>\uba40\ud2f0\uc4f0\ub808\ub4dc \ub3d9\uae30\ud654\ub97c \uc2e0\uacbd\uc4f4 \ub9cc\ud07c, \ud14c\uc2a4\ud2b8 \ucf54\ub4dc\ub3c4 \uba40\ud2f0 \uc4f0\ub808\ub4dc\ub85c \uad6c\uc131\ud574 \ubd24\ub2e4. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"rest\" class=\"language-rest\">[bato] bato, hi! I'm bato - 2020-05-03 16:38:11.351498\n[wan-tae] bato, hi! I'm bato - 2020-05-03 16:38:11.351498\n[jung-a] bato, hi! I'm bato - 2020-05-03 16:38:11.351498\n[bato] wan-tae, Do you see my chat? - 2020-05-03 16:38:11.745049\n[wan-tae] wan-tae, Do you see my chat? - 2020-05-03 16:38:11.745049\n[jung-a] wan-tae, Do you see my chat? - 2020-05-03 16:38:11.745049\n[bato] jung-a, hi! I'm jung-a - 2020-05-03 16:38:11.948585\n[wan-tae] jung-a, hi! I'm jung-a - 2020-05-03 16:38:11.948585\n[jung-a] jung-a, hi! I'm jung-a - 2020-05-03 16:38:11.948585\n[wan-tae] wan-tae, Hello? - 2020-05-03 16:38:13.761212<\/code><\/pre>\n\n\n\n<p>3\uac1c\uc758 \uc4f0\ub808\ub4dc\uc5d0\uc11c \ub300\ud654\uac00 \uc624\uac00\ub294\uac78 \ubcfc \uc218 \uc788\uace0, disconnect \uc774\ud6c4\uc5d0\ub294 \ub300\ud654\ub97c \ubc1b\uc9c0 \uc54a\ub294\uac78 \ub9c8\uc9c0\ub9c9\uc5d0 \ud655\uc778\uac00\ub2a5\ud558\ub2e4. <\/p>\n\n\n\n<hr class=\"wp-block-separator is-style-dots\"\/>\n\n\n\n<p> Observer pattern\uc740 \uc790\uc8fc \ubcf4\ub358 \ud328\ud134\uc774\ub77c \uc27d\uac8c\ub9cc \uc0dd\uac01\ud588\ub294\ub370, \ubb38\uc11c\uc758 \ub0b4\uc6a9\uacfc \uc2e4\uc81c \uc5b8\uc5b4\ub4e4 \uad6c\ud604\uc5d0 \ucc28\uc774\uac00 \uc788\uc5b4 \uace0\ubbfc\uc774 \uc880 \ub9ce\uc558\ub2e4. \uc9c4\uc9dc, \ub0a0\ub85c \uba39\uc73c\ub824 \ud588\ub358 \ud328\ud134\uc778\ub370, \uc774\ub807\uac8c \ubc29\ub300\ud55c \ub0b4\uc6a9\uc774 \uc788\uc744\uc904 \ubab0\ub790\ub2e4. \uc6cc\ub099 \ud754\ud558\uac8c \uc4f0\uc774\ub2e4\ubcf4\ub2c8 \uc5b8\uc5b4\ub808\ubca8\uc5d0\uc11c \uc9c0\uc6d0\ud558\ub294 \ub0b4\uc6a9\ub4e4\ub3c4 \ub9ce\uc740\ub370 \ub2e4 \ub098\uc5d0\uac90 \ub0af\uc120 \ub0b4\uc6a9\ub4e4\uc774\uc5c8\ub2e4. \uad6c\ud604 \ubc29\ubc95\ub3c4 \uac00\uc9c0\uac01\uc0c9\uc778\ub370, \uadf8\uac83\ub4e4 \uc870\ucc28 \uc0dd\uac01\ucc98\ub7fc \uac04\ub2e8\ud55c \uad6c\ud604\ub4e4\uc774 \uc544\ub2c8\uae30\ub3c4 \ud588\uace0. C#\uc758 IDisposable\uc740 Observer Pattern\uc758 \uaebc\ub9bc\uc9c1\ud55c \ubd80\ubd84\uc5d0 \uae54\ub054\ud55c \ub2f5\uc744 \uc8fc\uae34 \ud588\uc9c0\ub9cc, \uad6c\ud604\uc774 \ub108\ubb34 \ubc88\uc7a1\ud588\ub2e4. \ub610 \uc2e4\ubb34\uc5d0\uc11c\ub294 \ubc31\ud37c \ub3d9\uae30\ud654 \ubb38\uc81c\ub97c \ubb34\uc2dc\ud560 \uc218 \uc5c6\uc744\ud150\ub370, \uc774\uc5d0 \ub300\ud55c \ub0b4\uc6a9\ub3c4 \ub9c8\uc9c0\ub9c9 Python\uad6c\ud604\uc5d0 \uac00\uc11c\uc57c \uc0b4\ud3b4\ubd24\ub2e4. \ubcbd\uc5d0 \ub9c9\ud790 \ub54c\ub9c8\ub2e4\uac00 \ubbf8\ub8e8\ub2e4 \ubcf4\ub2c8 \uc774 \ud3ec\uc2a4\ud305 \ud558\ub098\uc5d0 3\uc77c\uc774 \uac78\ub838\ub294\ub370, \uadf8\ub7f4\ub9cc\ud55c \uc8fc\uc81c\uac00 \uc544\ub2c8\uc5c8\ub098 \uc0dd\uac01\ub418\ub124.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\ube14\ub85c\uadf8\ub098 \uc720\ud29c\ube0c\ub97c \ubcf4\uba74, \uad6c\ub3c5\uc744 \ud574\ub193\uace0 \uc0c8\uae00\uc774\ub098 \uc601\uc0c1\uc774 \uc62c\ub77c\uc62c \ub54c \uc54c\ub78c\uc744 \ubc1b\ub294\ub2e4. \uc774\ub7f0 \uc0c1\ud638\uc791\uc6a9\uc744 \uac8c\uc2dc-\uad6c\ub3c5(Publish-Subscribe)\uad00\uacc4\ub77c\uace0 \ud55c\ub2e4. Observer pattern\uc740 \uc774\ucc98\ub7fc observer\ub97c \ub4f1\ub85d\ud574\ub193\uace0 \ub300\uc0c1\uc758 \ubcc0\ud654\uc5d0 \ub300\ud55c \uc54c\ub78c\uc744 \ubc1b\ub294 \uacbd\uc6b0\uc5d0 \uc801\uc6a9\ub418\ub294 design pattern\uc774\ub2e4. Observer pattern\uc740 callback\uc758 \ud615\ud0dc\ub85c \uc774\ubbf8 \ub9ce\uc740 \uacf3\uc5d0\uc11c \uc0ac\uc6a9\ud558\uace0 \uc788\uace0 \uacbd\ud5d8\ud574\ubd24\uc744 \uac83\uc774\ub2e4. \uc548\ub4dc\ub85c\uc774\ub4dc\uc5d0\uc11c BroadcastReceiver\ub97c \ub4f1\ub85d\ud574 \uc0ac\uc6a9\ud558\ub294 \uac83\ub3c4 \uc774\ub7f0 \ud615\ud0dc\uc758 \ud558\ub098\ub77c\uace0 \ubcfc \uc218 \uc788\ub2e4. \ud558\ub098\uc758 \ub370\uc774\ud130\ub97c \ub450\uace0 \uc5ec\ub7ec\ubc29\uc2dd\uc758 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,6,34,33],"tags":[104,38,168,116,35,118,128,21,41,55],"class_list":["post-336","post","type-post","status-publish","format-standard","hentry","category-etc","category-unity","category-kotlin","category-python","tag-design-pattern","tag-kotlin","tag-observer","tag-oop","tag-python","tag-unity-2","tag-design-pattern-kr","tag-unity","tag-kotlin-kr","tag-python-kr"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts\/336","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/comments?post=336"}],"version-history":[{"count":22,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts\/336\/revisions"}],"predecessor-version":[{"id":557,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts\/336\/revisions\/557"}],"wp:attachment":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/media?parent=336"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/categories?post=336"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/tags?post=336"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}