我如何抽象出 Clojure 中的实现细节?
How do I abstract away implementation details in Clojure?
我想将持久层的细节隐藏在某种接口后面。在 Java 中,我只是创建一个接口并在某种启动函数中选择正确的实现。我仍在努力研究如何在 Clojure 中做到这一点。我在这里不一定需要任何类型安全,我相信我的单元测试可以找到那里的任何问题。我能想到的最好的办法是创建一个包含具有特定键的匿名函数的映射,如下所示:
(def crux-db {
:get-by-id (fn [id] (get-obj...))
:save (fn [id obj] (store-obj...))
})
(def fs-db {
:get-by-id (fn [id] (get-obj...))
:save (fn [id obj] (store-obj...))
})
如果我没有遗漏什么,这将允许我通过定义 (def db crux-db)
或 (def db fs-db)
来替换数据库实现,只要所有函数都存在于所有实现映射中。不知何故,我觉得这不是 clojure 的方式,但我无法确定。还有其他方法吗?
Protocols 是一种方法。它们让您定义应该有哪些功能。和
你可以稍后为不同的事情实现它们,例如
defrecord
.
A protocol is a named set of named methods and their signatures, defined using defprotocol:
(defprotocol AProtocol
"A doc string for AProtocol abstraction"
(bar [a b] "bar docs")
(baz [a] [a b] [a b c] "baz docs"))
- No implementations are provided
- Docs can be specified for the protocol and the functions
- The above yields a set of polymorphic functions and a protocol object
- all are namespace-qualified by the namespace enclosing the definition
- The resulting functions dispatch on the type of their first argument, and thus must have at least one argument
- defprotocol is dynamic, and does not require AOT compilation
- defprotocol will automatically generate a corresponding interface, with the same name as the protocol, e.g. given a protocol my.ns/Protocol, an interface my.ns.Protocol. The interface will have methods corresponding to the protocol functions, and the protocol will automatically work with instances of the interface.
既然你在代码中提到了关键点,你可以看看他们是如何做到的
用它
here
然后使用 defrecord
s 实现 一些
他们
有几种方法可以实现这一点。一种方法是使用 protocols. The other way would be to just use higher-order functions,您可以在其中“注入”特定函数并像这样公开它:
(defn get-by-id-wrapper [implementation]
(fn [id]
(implementation id)
...))
(defn cruxdb-get-by-id [id]
...)
(def get-by-id (get-by-id-wrapper cruxdb-get-by-id))
我想将持久层的细节隐藏在某种接口后面。在 Java 中,我只是创建一个接口并在某种启动函数中选择正确的实现。我仍在努力研究如何在 Clojure 中做到这一点。我在这里不一定需要任何类型安全,我相信我的单元测试可以找到那里的任何问题。我能想到的最好的办法是创建一个包含具有特定键的匿名函数的映射,如下所示:
(def crux-db {
:get-by-id (fn [id] (get-obj...))
:save (fn [id obj] (store-obj...))
})
(def fs-db {
:get-by-id (fn [id] (get-obj...))
:save (fn [id obj] (store-obj...))
})
如果我没有遗漏什么,这将允许我通过定义 (def db crux-db)
或 (def db fs-db)
来替换数据库实现,只要所有函数都存在于所有实现映射中。不知何故,我觉得这不是 clojure 的方式,但我无法确定。还有其他方法吗?
Protocols 是一种方法。它们让您定义应该有哪些功能。和
你可以稍后为不同的事情实现它们,例如
defrecord
.
A protocol is a named set of named methods and their signatures, defined using defprotocol:
(defprotocol AProtocol "A doc string for AProtocol abstraction" (bar [a b] "bar docs") (baz [a] [a b] [a b c] "baz docs"))
- No implementations are provided
- Docs can be specified for the protocol and the functions
- The above yields a set of polymorphic functions and a protocol object
- all are namespace-qualified by the namespace enclosing the definition
- The resulting functions dispatch on the type of their first argument, and thus must have at least one argument
- defprotocol is dynamic, and does not require AOT compilation
- defprotocol will automatically generate a corresponding interface, with the same name as the protocol, e.g. given a protocol my.ns/Protocol, an interface my.ns.Protocol. The interface will have methods corresponding to the protocol functions, and the protocol will automatically work with instances of the interface.
既然你在代码中提到了关键点,你可以看看他们是如何做到的
用它
here
然后使用 defrecord
s 实现 一些
他们
有几种方法可以实现这一点。一种方法是使用 protocols. The other way would be to just use higher-order functions,您可以在其中“注入”特定函数并像这样公开它:
(defn get-by-id-wrapper [implementation]
(fn [id]
(implementation id)
...))
(defn cruxdb-get-by-id [id]
...)
(def get-by-id (get-by-id-wrapper cruxdb-get-by-id))