【问题标题】:Mapping an objected oriented model to Clojure将面向对象模型映射到 Clojure
【发布时间】:2013-02-13 05:25:34
【问题描述】:

假设我们正在使用一种面向对象的语言,并且有两个类 X 和 Y,并且这些类之间存在双向关系。

所以 X 的实例可以指向 Y 的实例,反之亦然。

在 Clojure 中,类通常转换为地图,因此我们可以:

{:type :x :name "instance of X"}
{:type :y :name "instance of Y"}

我们如何表示这些“对象”之间的双向关系,而不使用“外键”之类的东西?还是这通常是直接委托给数据库的?

【问题讨论】:

    标签: oop clojure


    【解决方案1】:

    在 Clojure 中看到深度嵌套的映射是很常见的,它对应于面向对象语言中的分层对象树,例如

    {:type :x
     :name "instance of X"
     :y {:type :y
         :name "instance of Y"}}
    

    事实上,这很常见,以至于clojure.core 提供了get-inassoc-inupdate-in 等核心函数,以方便使用此类结构。

    当然,当被建模的对象之间存在自然的层次结构或所有权关系时,这种方法效果最好。在循环引用的情况下,这种结构会崩溃(假设您坚持使用持久数据结构)——要了解原因,请尝试构建一个包含自身作为值的 Clojure 映射。

    我通常看到处理这种情况的方法是使用atom 引入一个间接层:

    (def x {:type :x, :name "x instance", :y (atom nil)})
    (def y {:type :y, :name "y instance", :x (atom nil)})
    (set! *print-level* 3) ;; do this in the REPL to avoid stack overflow
                           ;; when printing the results of the following calls
    (reset! (:y x) y)
    (reset! (:x y) x)
    

    【讨论】:

    • 不会 refs 更适合这个,因为他们的变化可以协调?
    • 顺便说一句,这个代码在我尝试时会导致“*Error clojure.lang.RT.toArray (RT.java:1544)”
    • 取决于应用程序。如果您只想在创建地图时设置一次值,则 atom 可能就足够了。如果您要进行大量更新并希望确保图形的一致性,那么是的,您可能应该使用 refs。堆栈溢出可能来自打印结果,因为打印原子会显示它的值 - 不确定*print-level* 是否会影响它。
    • 刚刚检查过 - 您可以在 REPL 中调用 (set! *print-level* 3) 以避免堆栈溢出。