2012-02-28 18 views
10

मैं अधिक संक्षिप्त और बेवकूफ इंटरफेस प्रदान करने के लिए ब्रेनट्री जावा लाइब्रेरी के लिए क्लोजर रैपर लिख रहा हूं।क्लोजर मैक्रो?

(transaction-request :amount 10.00 :order-id "user42") 

मुझे पता है this question में दिखाया गया है मैं स्पष्ट रूप से यह कर सकते हैं,:: मैं जावा जल्दी से और संक्षेप में वस्तुओं, जैसे दृष्टांत के लिए कार्य करता है प्रदान करना चाहते हैं

(defn transaction-request [& {:keys [amount order-id]}] 
    (doto (TransactionRequest.) 
    (.amount amount) 
    (.orderId order-id))) 

लेकिन इसके लिए दोहराव है पैरामीटर वैकल्पिक होने पर कई वर्ग और अधिक जटिल हो जाते हैं। जाहिर है

(defn set-obj-from-map [obj m] 
    (doseq [[k v] m] 
    (clojure.lang.Reflector/invokeInstanceMethod 
     obj (name k) (into-array Object [v]))) 
    obj) 

(defn transaction-request [& {:as m}] 
    (set-obj-from-map (TransactionRequest.) m)) 

(defn transaction-options-request [tr & {:as m}] 
    (set-obj-from-map (TransactionOptionsRequest. tr) m)) 

, मैं अगर संभव हो प्रतिबिंब से बचने के लिए करना चाहते हैं: प्रतिबिंब का उपयोग करना, यह बहुत अधिक संक्षेप में इन कार्यों को परिभाषित करना संभव है। मैंने set-obj-from-map के मैक्रो संस्करण को परिभाषित करने का प्रयास किया लेकिन मेरा मैक्रो-फू पर्याप्त मजबूत नहीं है। here समझाया गया है कि इसे शायद eval की आवश्यकता है।

क्या प्रतिबिंब का उपयोग किए बिना रनटाइम पर निर्दिष्ट जावा विधि को कॉल करने का कोई तरीका है?

अग्रिम धन्यवाद!

अपडेट किए गए समाधान:

जूस्ट से सलाह के बाद, मैं एक ऐसी ही तकनीक का उपयोग कर समस्या को हल करने में सक्षम था। एक मैक्रो संकलन-समय पर प्रतिबिंब का उपयोग करता है यह पहचानने के लिए कि कक्षा में कौन से सेटर तरीके हैं और फिर मानचित्र में परम की जांच करने के लिए फॉर्म को थूकते हैं और विधि को इसके मूल्य के साथ कॉल करते हैं।

; Find only setter methods that we care about 
(defn find-methods [class-sym] 
    (let [cls (eval class-sym) 
     methods (.getMethods cls) 
     to-sym #(symbol (.getName %)) 
     setter? #(and (= cls (.getReturnType %)) 
         (= 1 (count (.getParameterTypes %))))] 
    (map to-sym (filter setter? methods)))) 

; Convert a Java camelCase method name into a Clojure :key-word 
(defn meth-to-kw [method-sym] 
    (-> (str method-sym) 
     (str/replace #"([A-Z])" 
        #(str "-" (.toLowerCase (second %)))) 
     (keyword))) 

; Returns a function taking an instance of klass and a map of params 
(defmacro builder [klass] 
    (let [obj (gensym "obj-") 
     m (gensym "map-") 
     methods (find-methods klass)] 
    `(fn [~obj ~m] 
     [email protected](map (fn [meth] 
       `(if-let [v# (get ~m ~(meth-to-kw meth))] (. ~obj ~meth v#))) 
       methods) 
     ~obj))) 

; Example usage 
(defn transaction-request [& {:as params}] 
    (-> (TransactionRequest.) 
    ((builder TransactionRequest) params) 
    ; some further use of the object 
)) 
+0

पसंद करेंगे क्या बिना प्रतिबिंब? लगभग निश्चित रूप से नहीं। –

+1

ठीक है, किसी मैक्रो का उपयोग करके प्रतिबिंब_ के बिना विधि कॉल में मानचित्र का अनुवाद करना संभव है। मैंने केवल यह महसूस करने पर प्रतिबिंब का उपयोग किया कि मैक्रो नक्शा धारण करने वाला प्रतीक नहीं ले सकता है, बल्कि केवल कच्चा नक्शा ही है। मुझे यह कहने में शायद अधिक स्पष्ट होना चाहिए कि मैं _runtime_ पर प्रतिबिंब से बचना चाहता हूं, जैसे @ joost-diepenmaat नीचे वर्णित है। – bkirkbri

उत्तर

8

आप संकलन समय पर प्रतिबिंब का उपयोग कर सकते ~ तो ~ फ़ील्ड नाम यह पता लगाने की जब तक आप वर्ग आप से साथ काम कर रहे पता है, और:

यहाँ मैक्रो और एक उदाहरण फायदा नहीं है उस से "स्थैतिक" सेटर्स उत्पन्न करें। मैंने कुछ कोड लिखा है जो थोड़ी देर पहले गेटर्स के लिए बहुत कुछ करता है कि आपको दिलचस्प लगेगा। https://github.com/joodie/clj-java-fields देखें (विशेष रूप से, https://github.com/joodie/clj-java-fields/blob/master/src/nl/zeekat/java/fields.clj में डीफ़-फ़ील्ड मैक्रो)।

+0

यह एक साफ दृष्टिकोण है। – amalloy

+0

धन्यवाद, यह वास्तव में सहायक था। मुझे अपने दृष्टिकोण को ऊपर-नीचे चालू करने और किसी भी नक्शा पैराम लेने के बजाय रनटाइम पर प्रतिबिंबित करने की बजाय, संकलन समय पर गणना करने वाले सेटर्स की आवश्यकता होती है। एक जोड़ा बोनस यह है कि केवल वैध पैरा की जांच की जाती है। – bkirkbri

1

मैक्रो के रूप में सरल हो सकता है:

(defmacro set-obj-map [a & r] `(doto (~a) [email protected](partition 2 r))) 

लेकिन यह अपने कोड देखो बनाना होगा जैसे:

(set-obj-map TransactionRequest. .amount 10.00 .orderId "user42") 

कौन सा मुझे लगता है कि नहीं है आप :)

+0

सच है, यह वाक्यविन्यास आदर्श नहीं है और आपके फ़ंक्शन के कॉलर्स द्वारा भी इसका उपयोग नहीं किया जा सकता है। आपको अभी भी इस वाक्यविन्यास में कॉलर तर्कों को मैप करने की आवश्यकता होगी। – bkirkbri

+0

वाह अद्भुत। यही कारण है कि मैं सीखने के क्लोजर प्यार करता हूँ। – Core

संबंधित मुद्दे