ivo*_*ivo 23 lisp scheme functional-programming clojure observer-pattern
假设我想使用OO编程语言实现事件总线.我可以这样做(伪代码):
class EventBus
listeners = []
public register(listener):
listeners.add(listener)
public unregister(listener):
listeners.remove(listener)
public fireEvent(event):
for (listener in listeners):
listener.on(event)
Run Code Online (Sandbox Code Playgroud)
这实际上是观察者模式,但用于应用程序的事件驱动控制流.
您将如何使用函数式编程语言(例如lisp风格之一)实现此模式?
我问这个是因为如果一个人不使用对象,人们仍然需要某种状态来维护所有听众的集合.此外,由于听众集合随着时间的推移而变化,因此无法创建纯粹的功能解决方案,对吧?
LiK*_*Kao 24
对此有一些评论:
我不确定它是如何完成的,但有一种称为" 功能反应式编程 "的东西可用作许多函数式语言的库.这实际上或多或少是正确的观察者模式.
此外,观察者模式通常用于通知状态的变化,如在各种MVC实现中那样.但是在函数式语言中没有直接的方法来进行状态更改,除非你使用monad等一些技巧来模拟状态.但是,如果使用monad模拟状态更改,您还可以获得可以在monad中添加观察器机制的点.
根据您发布的代码判断,您实际上正在进行事件驱动编程.因此,观察者模式是在面向对象语言中获取事件驱动编程的典型方式.因此,您有一个目标(事件驱动编程)和面向对象世界中的工具(观察者模式).如果您想充分利用函数式编程,那么您应该检查可用于实现此目标的其他方法,而不是直接从面向对象的世界移植工具(它可能不是函数式语言的最佳选择).只需检查这里有哪些其他工具,您可能会找到更适合您目标的东西.
Sco*_*ott 22
如果Observer模式主要是关于发布者和订阅者,那么Clojure有几个你可以使用的函数:
add-watch函数有三个参数:引用,监视功能键和在引用更改状态时调用的监视功能.
很明显,由于可变状态的变化,这不是纯粹的功能(正如你明确要求的那样),但是add-watcher会给你一种对事件作出反应的方法,如果那是你正在寻找的效果,就像这样:
(def number-cats (ref 3))
(defn updated-cat-count [k r o n]
;; Takes a function key, reference, old value and new value
(println (str "Number of cats was " o))
(println (str "Number of cats is now " n)))
(add-watch number-cats :cat-count-watcher updated-cat-count)
(dosync (alter number-cats inc))
Run Code Online (Sandbox Code Playgroud)
输出:
Number of cats was 3
Number of cats is now 4
4
Run Code Online (Sandbox Code Playgroud)
此外,由于听众集合随着时间的推移而变化,因此无法创建纯粹的功能解决方案,对吧?
这不是一个问题 - 通常,只要您在命令式解决方案中修改对象的属性,就可以在纯功能解决方案中使用新值计算新对象.我相信实际的事件传播有点问题 - 它必须由一个接受事件的函数,整组潜在的观察者加上EventBus,然后过滤掉实际的观察者并返回一组全新的对象来实现.通过事件处理函数计算的新观察者状态.非观察者当然在输入和输出集中是相同的.
如果那些观察者在响应他们的on方法(这里:函数)被调用时生成新事件会变得有趣- 在这种情况下,你需要递归地应用函数(可能允许它接受多个事件),直到它不再产生事件为止处理.
通常,该函数将获取事件和一组对象,并返回新的对象集,其中新的状态表示由事件传播产生的所有修改.
TL; DR:我认为以纯粹的功能方式建模事件传播是复杂的.
我建议创建一个包含一组侦听器的引用,每个侦听器都是一个对事件起作用的函数.
就像是:
(def listeners (ref #{}))
(defn register-listener [listener]
(dosync
(alter listeners conj listener)))
(defn unregister-listener [listener]
(dosync
(alter listeners disj listener)))
(defn fire-event [event]
(doall
(map #(% event) @listeners)))
Run Code Online (Sandbox Code Playgroud)
请注意,您在此处使用了可变状态,但这是正常的,因为您尝试明确解决的问题需要跟踪一组侦听器的状态.
请注意,感谢CAMcCann的评论:我正在使用一个"ref"来存储一组活跃的监听器,它具有很好的奖励属性,该解决方案对于并发性是安全的.所有更新都由(dosync ....)构造中的STM事务保护.在这种情况下,它可能是矫枉过正(例如,一个原子也可以做到这一点),但这可能会在更复杂的情况下派上用场,例如,当您注册/取消注册一组复杂的侦听器并希望更新发生在一个单独的时候,线程安全的转换.
| 归档时间: |
|
| 查看次数: |
5635 次 |
| 最近记录: |