2017-07-07 65 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

相关问题