用于封装的Clojure对象和高阶函数

时间:2011-05-15 14:03:33

标签: clojure encapsulation higher-order-functions

这是我earlier question的后续行动。

我在阅读Let Over Lambda时提出了一个奇怪的对象方案,可以想到没有优于协议的优势,但想获得意见。我正在探索使用高阶函数和封装。

(defn new-person
  "Construct a new Person object and return a Map of accessor methods."
  [init-first-name init-last-name init-age]
  (let [first-name (ref init-first-name)
        last-name (ref init-last-name)
        age (ref init-age)]
    {:get-first-name #(@first-name)
     :set-first-name #(dosync (ref-set first-name %1))
     :get-last-name #(@last-name)
     :set-last-name #(dosync (ref-set last-name %1))
     :get-age #(@age)
     :set-age #(dosync (ref-set age %1))}))

我可以使用这样的对象:

(def fred (new-person "fred" "flintstone" 42))

并以这种方式检索访问器方法:

(fred :get-age)

但我无法弄清楚如何调用访问者。

创建的对象是线程安全的,因为“实例”变量的所有变异都发生在STM中。

更新:新版和改进版:

(defn new-person
  "Construct a new Person object and return a Map of accessor methods."
  [init-first-name init-last-name init-age]
  (let [first-name (ref init-first-name)
        last-name (ref init-last-name)
        age (ref init-age)]
    {:first-name 
        (fn
          ([] @first-name)
          ([val] (dosync (ref-set first-name val))))
     :last-name
        (fn
          ([] @last-name)
          ([val] (dosync (ref-set last-name val))))
     :age
        (fn
          ([] @age)
          ([val] (dosync (ref-set age val))))}))

3 个答案:

答案 0 :(得分:5)

也许不是100%回答你的问题,但你试图做的不是Clojure非常惯用。 “标准”解决方案类似于:

(defrecord Person [first-name last-name age])
(def fred (Person. "fred" "flintstone" 42))
(fred :age)

看起来你正在强迫OO可变状态进入Clojure'对象'

答案 1 :(得分:3)

与您的后续问题相同,将表单包装在另一组括号中。当内部表单返回函数时,它与返回函数的符号相同。规则是括号中的第一个表单将始终被查找为特殊形式,宏或函数。你需要像引用这样的东西来阻止这种行为。

user=> (fred :get-age)
#<user$new_person$fn__531 user$new_person$fn__531@c4afc4>
user=> ((fred :get-age))
42

相同
user=> (let [age-getter (fred :get-age)] (age-getter))
42

答案 2 :(得分:1)

Clojure哲学不是封装对记录字段本身的访问。封装应该在更高级别上进行,例如在与该记录一起使用的函数集中。见Clojure - datatypes

  

信息封装是愚蠢的字段是公共的,使用协议/接口来避免依赖

相关问题