2016-06-18 6 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

감사합니다. 총 3 가지 메소드를 가진 단 하나의 객체 일 뿐이므로 두 개의 프로토콜 (한 프로토콜에서 2 개의 메소드와 다른 프로토콜에서 2 개의 메소드)을 구현하므로 매크로가 없어도 나쁘지 않습니다. 난 그냥 핵심 Clojure lib에서 아무것도 놓치지 않고 있는지 확인 싶었어요. 나는 메소드의 수가 더 많았다면 확실히 매크로로 갈 것이다. 개체는 외부 라이브러리에서 만들어 지므로이 개체를 약간 사용자 지정해야합니다. – Pol