2016-06-18 5 views

答えて

3

Clojureには、オブジェクトの振る舞いをデフォルトで別のオブジェクトに委譲する組み込みの構造がないので、デコレータを作成する必要があります(これはプロトタイプの継承と呼ばれています)。

しかし、それは、それは退屈である必要がありますという意味ではありません - あなたは、ほとんどの作業を自動化するマクロとリフレクションを使用することができます。ここではコンセプトの証明です:

あなたがそれを使用する方法
(defmacro decorator 
    [clazz proto & fs] 
    (let [proto-name (gensym "proto") 
     methods (->> (clojure.reflect/reflect (resolve clazz)) 
        :members 
        (filter #(instance? clojure.reflect.Method %)) 
        (map (fn [{:keys [name parameter-types]}] 
         [name (count parameter-types)])) 
        set) 
     to-delegate (clojure.set/difference 
         methods 
         (->> fs 
         (map (fn [[name params]] 
           [name (count params)])) 
         set)) 
     method-bodies 
     (concat 
      fs ;; these are our own definitions 
      (for [[name n-params] to-delegate] 
      (let [params (->> (range n-params) 
          (map #(gensym (str "x" %))))] 
       `(~name [[email protected]] 
       (. ~proto-name (~name [email protected]))) ;; this is where we delegate to the prototype 
      )))] 
    `(let [~proto-name ~proto] 
     (proxy 
     [~clazz] [] 
     [email protected](->> method-bodies (group-by first) (sort-by first) 
      (map (fn [[name bodies]] 
        `(~name [email protected](for [[name & rest] bodies] 
           rest)))))) 
     ))) 

(decorator 
    java.util.Collection 
    [:a :b :c] 
    (size [] -1)) 
=> #object[user.proxy$java.lang.Object$Collection$4e41253d 
     0x1eae8922 
     "[email protected]"] 

と拡張:

(macroexpand-1 '(decorator 
        java.util.Collection 
        [:a :b :c] 
        (size [] -1))) 
=> 
(clojure.core/let 
[proto28109 [:a :b :c]] 
(clojure.core/proxy 
    [java.util.Collection] 
    [] 
    (add ([x028114] (. proto28109 (add x028114)))) 
    (addAll ([x028110] (. proto28109 (addAll x028110)))) 
    (clear ([] (. proto28109 (clear)))) 
    (contains ([x028118] (. proto28109 (contains x028118)))) 
    (containsAll ([x028116] (. proto28109 (containsAll x028116)))) 
    (equals ([x028119] (. proto28109 (equals x028119)))) 
    (hashCode ([] (. proto28109 (hashCode)))) 
    (isEmpty ([] (. proto28109 (isEmpty)))) 
    (iterator ([] (. proto28109 (iterator)))) 
    (parallelStream ([] (. proto28109 (parallelStream)))) 
    (remove ([x028117] (. proto28109 (remove x028117)))) 
    (removeAll ([x028115] (. proto28109 (removeAll x028115)))) 
    (removeIf ([x028111] (. proto28109 (removeIf x028111)))) 
    (retainAll ([x028112] (. proto28109 (retainAll x028112)))) 
    (size ([] -1)) 
    (spliterator ([] (. proto28109 (spliterator)))) 
    (stream ([] (. proto28109 (stream)))) 
    (toArray ([] (. proto28109 (toArray))) ([x028113] (. proto28109 (toArray x028113)))))) 

この実装はproxy句を生成し、それはまた、行うことができますreify

+0

ありがとうございました。それは2つのプロトコル(1つのプロトコルから2つのメソッドと別のプロトコルから2つのメソッド)を実装する、合計3つのメソッドを持つ唯一のオブジェクトですので、マクロなしでそれほど悪くはありません。私はちょうど私がコアClojureライブラリから何かを逃していないことを確認したかった。メソッドの数が増えれば、私は確かにマクロに行くだろう。オブジェクトは外部ライブラリで作成され、このオブジェクトを少しだけカスタマイズする必要があります。 – Pol

関連する問題