Clojure中的面向方面编程

Clojure中的面向方面编程,clojure,jvm,aop,Clojure,Jvm,Aop,如何在Clojure中实现面向方面的编程?我们在Clojure需要AOP吗? 假设我们想要纯香草Clojure解决方案(无AspectJ) AOPIMHO只是某些静态编程语言的产物。AFAIKS通常只是一堆非标准的编译器扩展。我还没有看到任何AOP的应用程序不能用更动态的语言更好地解决。Clojure当然是足够动态的,甚至不考虑宏 我可能错了,但如果是这样,我需要看到一个在纯clojure中无法实现的实际AOP用例 编辑:我只是想澄清一点:我拒绝将elisp的建议视为面向方面的。在动态语言中,这

如何在Clojure中实现面向方面的编程?我们在Clojure需要AOP吗?

假设我们想要纯香草Clojure解决方案(无AspectJ)

AOPIMHO只是某些静态编程语言的产物。AFAIKS通常只是一堆非标准的编译器扩展。我还没有看到任何AOP的应用程序不能用更动态的语言更好地解决。Clojure当然是足够动态的,甚至不考虑宏

我可能错了,但如果是这样,我需要看到一个在纯clojure中无法实现的实际AOP用例

编辑:我只是想澄清一点:我拒绝将elisp的建议视为面向方面的。在动态语言中,这些只是在需要时使用的技术,不需要语言支持,只需要重新绑定函数定义——所有Lisp都支持这一点


没有必要将它们视为特殊的-您可以轻松地在clojure中定义自己的类似defadvice的函数。例如,请参阅,它实际上已被弃用,因为您通常甚至不需要它。

面向方面编程是在Java中实现关注点分离的一种很好的方法。Clojure的可组合抽象很好地实现了这一点。另见。这一主题在本书中涵盖得很好


关于另一个名称为面向方面的Clojure的示例,请查看Ring web framework

面向方面编程通常用于向代码添加横切功能,否则这些代码将与业务逻辑毫无希望地交织在一起。一个很好的例子是日志记录——您并不希望日志记录代码分散在代码库中的任何地方

在Clojure中不需要AOP,因为在Clojure中使用其他技术很容易实现这一点

例如,您可以使用高阶函数“包装”具有横切功能的其他函数:

; a simple function - the "business logic"
(defn my-calculation [a b]
  (+ a b))

; higher order function that adds logging to any other function
(defn wrap-with-logging [func]
  (fn [& args]
    (let [result (apply func args)]
      (println "Log result: " result)
      result)))

; create a wrapped version of the original function with logging added
(def my-logged-calculation (wrap-with-logging my-calculation))

(my-logged-calculation 7 9)
=> Log result:  16
=> 16

好吧,你可以更轻松地与Clojure合作。在需要日志时,只需在函数中使用元数据即可通知我:

(defn^:记录我的计算
[甲、乙]
(+a b))
然后,您可以重新定义所有函数,自动包装它们并记录日志。本代码的一部分(连同以下展开功能):

(定义日志fn
[f主题严重性错误严重性]
(fn[&args]
(试试看
(如果严重
(让[r(应用f参数)]
(日志*主题{:args args,:ret r}严重性)
(r)
(应用f参数)
(捕捉例外e
(如果错误严重
(让[数据{:args args,:error(处理错误e),:severity error severity}]
(日志*主题数据错误严重性)
(e)
(抛e(()())))
(defn logfn ns
“包装函数调用以记录调用或错误。
默认情况下,不执行任何操作。当出现任何:log或:log错误时,将启用日志记录。如果^:log,
仅登录错误(默认严重性错误)。
可以自定义日志严重性,例如^{:日志信息}或错误日志严重性。”
[ns别名]
(doseq[s(键(n个实习生))
:让[v(ns解析ns)
f@v
日志(->v meta:log)
日志错误(->v元:日志错误)]
:何时(和(如果有)
(->v meta:宏不可用)
(>v meta:not);;使其幂等
(或日志错误))]
(让[记录(如果(=记录为真)无记录)
日志错误(或日志错误“错误”)
带对数的f(对数)
(str别名“/”s)
日志
日志错误)]
(alter meta!(实习生ns s f-with-log)
(fn[x]
(->x
(关联:记录为真)
(助理:unlock@v(()()())))
(defn卸载
“还原日志fn ns。”
[ns]
(doseq[s(键(n个实习生))
:让[v(ns解析ns)]
:当(->v元:记录时)]
(让[f-不带对数(->v meta:未标记)]
(alter meta!(实习生ns s f-WITH-log)
(fn[x]
(->x
(dissoc:已记录)
(dissoc:unlock(()()())))
您只需调用
(log/logfn ns'my.namespace“some alias”)
,就可以使用日志(以及一些)包装所有内容

注:我上面的自定义记录器有一个
主题
,它是“某个别名/函数名” PS2:也用try/catch包装。
PS3:我不太喜欢这个。还原为显式日志。

与AOP相比,该示例的问题在于,现在开发人员必须调用新的包装器方法,而不是原始方法。理想情况下,可以通过调用原始方法来实现日志记录行为。这将更接近AOP提供的功能,对吗?@jcrossley3-如果您愿意,您可以随时使用(def my calculation(wrap with logging my calculation))重新定义原始函数……但重新计算并不安全。有关这方面更灵活的形式化,请参见Robert Hooke:@jcrossley3,这取决于如何使用AOP。例如,您可以在函数调用周围使用
环绕
运算符。这正是本文演示的内容。说宏是动态的,因为它们在编译时运行(它们是编译器的挂钩),这有点奇怪。更改宏时,需要重新编译调用宏的所有代码。在我的书中不是很动态……而且,AOP与MOP密切相关,MOP是公共Lisp的元对象协议(Pascal Constanza也以倡导CL的AOP而闻名)。你认为CommonLisp也是静态的吗?这个答案似乎是(未受过教育的)猜测的随机集合……我认为这个答案似乎暗示(对于一个不精通Clojure的人来说)Clojure在某种程度上消除了交叉关注,而无需任何额外的努力。关于这一点,米凯拉的回答更为明确,还包括一些例子,我认为这应该是公认的答案。f