在Clojure中为对象的特定实例创建代理 [英] Create a proxy for an specific instance of an object in clojure

查看:27
本文介绍了在Clojure中为对象的特定实例创建代理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个代理对象,它使用闭包(let/Proxy)向对象的一些方法添加一些功能。我可以做到这一点,但不幸的是,我必须重写原始对象中的所有方法,否则我会得到一个UnsupportedOpreationException。下面是一个示例: ;;实物

(def realcon (java.sql.DriverManager/getConnection "jdbc:h2:tcp://localhost:9092/test"))


(def con 
    (let [msg "FG>"
          xcon rcon]
        (proxy [java.sql.Connection] []
            (createStatement []
                (println msg) ;; access to closure context !
                (.createStatement xcon)))))

(def stmt (.createStatement con))
;;output FG>

(def rs (.executeQuery stmt "select count(*) from serie_sat"))

如果我从java.sql.Connection调用任何其他方法,我会得到UnsupportedOperationException异常,我可以手动代理所有方法,但可能有更好的方法!

谢谢

推荐答案

这里是使用reify而不是proxy的替代方法,因为根据docs的说法,它"在其约束不是禁止的所有情况下都是首选的。"

(defmacro override-delegate
  [type delegate & body]
  (let [d (gensym)
        overrides (group-by first body)
        methods (for [m (.getMethods (resolve type))
                      :let [f (-> (.getName m)
                                symbol
                                (with-meta {:tag (-> m .getReturnType .getName)}))]
                      :when (not (overrides f))
                      :let [args (for [t (.getParameterTypes m)]
                                   (with-meta (gensym) {:tag (.getName t)}))]]
                  (list f (vec (conj args 'this))
                    `(. ~d ~f ~@(map #(with-meta % nil) args))))]
    `(let [~d ~delegate]
       (reify ~type ~@body ~@methods))))


;; Modifying your example slightly...
(def realcon (java.sql.DriverManager/getConnection "jdbc:h2:tcp://localhost:9092/test"))
(def con 
  (let [msg "FG>"]
    (override-delegate java.sql.Connection realcon
      (createStatement [this]
        (println msg)
        (.createStatement realcon)))))
override-delegate宏期望主体包含您希望覆盖的方法的reify规范。您没有覆盖的任何内容都将在代理上调用。宏生成的所有reify规范都将包括每个方法的参数和返回值的类型提示。

我的实现有一个警告:它只检查body中的方法名称,而忽略重载方法的参数大小性/类型。因此,在上面的示例中,java.sql.Connection接口提供了多个createStatement重载,不会为con定义任何接受参数的重载。扩展宏以解决重载并不是太困难,但当我需要此行为时,我通常无论如何都必须覆盖所有这些行为。

这篇关于在Clojure中为对象的特定实例创建代理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆