这是一个过于简单的例子:
我可以封装一个实现细节,例如使用原子作为计数器:
(defn make-counter
([] (make-counter 0))
([init-val]
(let [c (atom init-val)]
{:get (fn [] @c)
:++ (fn [] (swap! c inc))})))
但这意味着我需要重新定义所有内容以添加功能(无继承):
(defn make-bi-counter
([] (make-bi-counter 0))
([init-val]
(let [c (atom init-val)]
{:get (fn [] @c)
:++ (fn [] (swap! c inc))
:-- (fn [] (swap! c dec))})))
然而,如果可以只扩展一个函数:
(assoc c :-- (env (:++ c) (fn [] (swap! c dec))))
(def c (make-counter))
(def b (make-bi-counter))
user=> ((:-- b))
-1
user=> ((:-- b))
-2
user=> ((:get b))
-2
或者我可以暴露原子并具有独立的功能:
(defn -- [a] (swap! a dec))
(def a (atom 0))
(-- a)
似乎最好的选择是放弃封装,如果'继承'(或者更准确地说:扩展)是可取的。
答案 0 :(得分:16)
是的,我认为习惯性的Clojure是将您的数据与您的函数分开,正是因为您可以稍后编写新函数来处理旧数据。
将函数与数据捆绑在一起也意味着您无法在不更改或重新生成所有数据结构的情况下更改函数,因为您将在整个地方存储这些匿名函数。在REPL上交互式开发,我不想每次更改函数时都要搜索所有数据结构来修复它们。哈希映射中的闭包是聪明的,但它们非常脆弱,除非有充分的理由,否则我不会走那条路。
定义你的界面(作为函数)只需要一些纪律,然后记住坚持你的界面而不是直接搞乱原子。目前还不清楚你会从强迫自己隐藏的东西中获得什么好处。
如果你想继承,多方法是一种很好的方法。
(defmulti getc type)
(defmulti ++ type)
(defmulti -- type)
(derive ::bi-counter ::counter)
(defn make-counter
([] (make-counter 0))
([init-val]
(atom init-val :meta {:type ::counter})))
(defn make-bi-counter
([] (make-bi-counter 0))
([init-val]
(atom init-val :meta {:type ::bi-counter})))
(defmethod getc ::counter [counter]
@counter)
(defmethod ++ ::counter [counter]
(swap! counter inc))
(defmethod -- ::bi-counter[counter]
(swap! counter dec))
e.g。
user> (def c (make-counter))
#'user/c
user> (getc c)
0
user> (def b (make-bi-counter))
#'user/b
user> (++ c)
1
user> (++ b)
1
user> (-- b)
0
user> (-- c)
; Evaluation aborted.
;; No method in multimethod '--' for dispatch value: :user/counter
答案 1 :(得分:0)
我确信这不是惯用的Clojure,但你绝对可以模拟受保护的变量。
在您的示例中, c 是模拟的私有变量。如果您希望它是受保护的变量,则需要以允许 make-counter 和 make-bi-counter 访问它的方式对其进行定义。将名为 secret 的哈希值传递给 make-counter 。如果机密包含 c ,请使用它。否则就创建自己的。
然后 make-bi-counter 可以创建一个密码对象,其中包含 c 并将其传递给制作计数器< / strong>构造函数。现在, make-bi-counter 和 make-counter 都可以访问相同的 c 。