Inheritance Clojure允许封装和继承,但我可以组合它们吗?

Inheritance Clojure允许封装和继承,但我可以组合它们吗?,inheritance,clojure,encapsulation,Inheritance,Clojure,Encapsulation,下面是一个过于简单的示例: 我可以封装实现细节,例如使用atom作为计数器: (defn make-counter ([] (make-counter 0)) ([init-val] (let [c (atom init-val)] {:get (fn [] @c) :++ (fn [] (swap! c inc))}))) 但这意味着我需要重新定义所有内容以添加功能(无继承): 然而,如果有可能只扩展一个功能: (assoc c :-- (env (:++

下面是一个过于简单的示例:

我可以封装实现细节,例如使用atom作为计数器:

(defn make-counter
  ([] (make-counter 0))
  ([init-val]
   (let [c (atom init-val)]
     {:get (fn [] @c)
      :++ (fn [] (swap! c inc))})))
但这意味着我需要重新定义所有内容以添加功能(无继承):

然而,如果有可能只扩展一个功能:

(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)

如果“继承”(或者更准确地说:扩展)是可取的,那么最好的选择似乎是放弃封装。

是的,我认为惯用的Clojure是将数据与函数分开,原因正是您以后可以编写新函数来处理旧数据

将函数与数据绑定还意味着您以后不能在不更改或重新生成所有数据结构的情况下更改函数,因为这些匿名函数将被存储在所有位置。在REPL中以交互方式开发,我不希望每次更改函数时都必须查找所有数据结构来修复它们。散列映射中的闭包很聪明,但它们非常脆弱,除非有很好的理由,否则我不会这么做

定义接口(作为函数)只需要一点点规范,然后记住要坚持使用接口,不要直接与atom混在一起。现在还不清楚强迫自己隐藏东西会给你带来什么好处

如果您想要继承,多方法是一种很好的方法

(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


我确信它不是惯用的Clojure,但您肯定可以模拟受保护的变量

在您的示例中,c是一个模拟的私有变量。如果您希望它是一个受保护的变量,则需要以允许make countermake bi counter访问它的方式定义它。将名为secret的散列传递到生成计数器中。如果secret包含c,则使用该密码。否则,创建您自己的


然后,make-bi-counter可以创建一个包含csecret对象,并将其传递给make-counter构造函数。现在make bi countermake counter都可以访问相同的c

我认为封装不是“对自己隐藏东西”。首先是要将您的设计传达给其他人。
(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))
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