2017-07-07 64 views
1

假設我需要製作一個簡單的計數器,並且每次調用此函數時都希望計數器增加,但這裏有一件不愉快的事情:定義的「計數器」不是本地的,我可以輕鬆地將其值從另一個空間,打破封裝。是否有任何製作本地'defonce'的方法? (Clojure)

(defn next [] 
    (defonce counter (atom 0)) 
    (println @counter) 
    (reset! counter (inc @counter))) 

很多人說,如果我放置'私人'中繼標記將是正確的。所以功能如下:

(defn next [] 
    (defonce ^:private counter (atom 0)) 
    (println @counter) 
    (reset! counter (inc @counter))) 

但我仍然可以從另一空間訪問'counter'。
有什麼辦法來實現這種封裝,或者它只在協議級別?

+0

您確定您可以訪問另一個ns的私有原子嗎? – mishadoff

+0

@mishadoff [是。](https://github.com/bbatsov/clojure-style-guide/blob/cb0be3a21c234fbb5bd152e3d67ffbf104140077/README.md#access-private-var) –

回答

4

這裏是你應該怎麼寫你的next功能:

(def ^{:arglists '([])} next 
    (let [counter (atom 0)] 
    #(let [after (swap! counter inc) 
      before (dec after)] 
     (println before) 
     after))) 

這是一樣的一個你的問題,但它是線程安全的,完全封裝counter原子。

+0

完美!但是......'^ {:arglists'([])}' - 這是爲了什麼? – errfrom

+0

@errfrom這不是真的有必要;嘗試'(clojure.repl/doc下一個)'有和沒有它,你可以決定是否要包括它。 –

1

私人的作品好,你不應該有其他的命名空間訪問

user> (ns a) 
nil 
a> (defonce ^:private counter (atom 0)) 
#'a/counter 
a> (defn next [] 
    (println @counter) 
    (swap! counter inc)) 
#'a/next 
a> (next) 
0 
1 
a> (next) 
1 
2 
a> (next) 
2 
3 
a> (next) 
3 
4 
a> (ns b) 
nil 
b> (require 'a) 
nil 
b> (a/next) 
4 
5 
b> (a/next) 
5 
6 
b> a/counter 
CompilerException java.lang.IllegalStateException: var: a/counter is not public 
b> (ns a) 
nil 
a> a/counter 
#object[clojure.lang.Atom 0x1ec64eb6 {:status :ready, :val 6}] 

也有一些小問題:

  1. 在納秒的頂層定義counter,而不是裏面的功能,無論是具有相同的效果,但頂級更清晰
  2. 更改reset!(swap! counter inc),它將是線程安全的
+0

有一個小小的誤解:我想反櫃檯只有在這個功能沒有從頂層訪問作爲本地綁定「讓」做。可能嗎? – errfrom

+0

@errfrom好吧,我不認爲你可以隱藏局部可變內部函數,而不能從外部訪問。使用defrecord或deftype代替(類似於在java中創建一個Counter對象) – mishadoff

+0

我修改了我的代碼,並決定將它的一部分放在一個單獨的模塊中,在頂層定義不會產生任何問題,所以基本上問題解決了。謝謝=) – errfrom

相關問題