【问题标题】:Is there any method of making local 'defonce'? (Clojure)有什么方法可以制作本地“defonce”吗? (Clojure)
【发布时间】:2023-03-21 01:08:01
【问题描述】:

假设我需要制作一个简单的计数器,并且我希望每次调用此函数时计数器都会递增,但有一件令人不快的事情:定义的“计数器”不是本地的,我可以轻松地从另一个空间更改它的值,这会破坏封装。

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

但我仍然可以从另一个空间访问“计数器”。
有什么方法可以实现这种封装还是仅在协议级别?

【问题讨论】:

  • 你确定你可以从另一个 ns 访问私有 atom 吗?
  • @mishadoff Yes.

标签: clojure counter local private encapsulation


【解决方案1】:

Private 很好用,你不应该从其他命名空间访问

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. 在ns的顶层定义counter,不在函数内部,两者作用相同,但顶层更清晰
  2. reset!更改为(swap! counter inc),它将是线程安全的

【讨论】:

  • 有一个小误解:我希望 counter 仅在此函数中,而不能像本地绑定“让”那样从顶层访问。有可能吗?
  • @errfrom 好吧,我不认为你可以在一个函数内隐藏本地可变,而不能从外部访问。改用 defrecord 或 deftype(类似于在 java 中创建 Counter 对象)
  • 我修改了我的代码,并决定我应该把它的一部分放在一个单独的模块中,在顶层定义不会产生任何问题,所以基本上问题解决了。谢谢=)
【解决方案2】:

以下是您应该如何编写next 函数:

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

这与您问题中的相同,只是它是线程安全的并且完全封装了counter 原子。

【讨论】:

  • 完美!但是...^{:arglists '([])} - 这是干什么用的?
  • @errfrom 这不是真的必要;试试(clojure.repl/doc next) 有和没有它,你可以决定是否要包含它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-21
  • 1970-01-01
  • 2015-07-02
  • 1970-01-01
  • 1970-01-01
  • 2020-07-01
  • 2020-03-06
相关资源
最近更新 更多