我正在开发一个clojure库,这在某种程度上需要是有状态的。为了不在抽象描述中花费太多单词,这里将是一个OOP示例,说明我如何能够使用库的API。
mylib = new lib();
state1 = mylib.getState();
mylib.continue();
state2 = mylib.getState();
mylib.continue();
state3 = mylib.getState();
[..]
(显然state1!= state2!= state3)
好的,怎么能用像Clojure这样的函数式语言来完成?
我想到了一种方法:
(require '(lib.core :as mylib))
(def state1 (mylib/start-state))
(def state2 (mylib/continue state1))
(def state3 (mylib/continue state2))
[..]
这种方法不会将状态保持分配给库端。我遇到的问题如下:我的状态确实保留了应该公开给API用户的信息。但它也保留了对下一个州的产生很重要的信息,但这与公众无关。
好吧,可能还有另一个函数(mylib/extract-relevant-data state1)
,它可以在oder中对状态进行后处理,以便"清洁使用"。
我真的很想学习在Clojure中我可以采用哪种方式来学习。
答案 0 :(得分:2)
方法1:重新实施OOP 。我将last-time-called
视为私人数据,将relevant state
视为您要向人们展示的内容。
(defn start-state []
(let [last-time-called (atom (new java.util.Date))
relevant-state (atom 1)
private-update (fn [] (swap! last-time-called (fn [_] (new java.util.Date))))
get-state (fn [] (do (private-update)
@relevant-state))
continue (fn [] (do (private-update)
(swap! relevant-state inc)
nil))]
{:get-state get-state :continue continue}))
演示:
stack-prj.hiddenState> (def mylib (start-state))
#'stack-prj.hiddenState/mylib
stack-prj.hiddenState> ((mylib :get-state))
1
stack-prj.hiddenState> ((mylib :continue))
nil
stack-prj.hiddenState> ((mylib :get-state))
2
stack-prj.hiddenState> ((mylib :continue))
nil
请注意get-state
和continue
可以last-time-called
访问{。}}。
方法2:简单数据上的纯函数。
(defn new-lib []
{:relevant-state 1
:last-time-called (new java.util.Date)})
(defn get-state [lib]
(lib :relevant-state))
(defn lib-continue [lib]
{:relevant-state (inc (lib :relevant-state))
:last-time-called (new java.util.Date)})
演示:
stack-prj.noHiddenState> (def mylib (new-lib))
#'stack-prj.noHiddenState/mylib
stack-prj.noHiddenState> (get-state mylib)
1
stack-prj.noHiddenState> (def ml2 (lib-continue mylib))
#'stack-prj.noHiddenState/ml2
stack-prj.noHiddenState> (get-state ml2)
2
请注意,使用方法2,当用户通过get-state
访问状态时,无法更新对象的私有变量。如果您需要此功能,那么方法1可以更好地满足您的需求;如果不这样做,那么方法2提供比第一种方法更干净,惯用且可维护的代码。