2016-05-28 5 views
1

ビルダーパターンを使用してJavaクラスとの相互運用を容易にするClojureマクロを作成しようとしています。文字列を使用してJava関数を呼び出すClojureマクロを作成する

これまで私が試したことは次のとおりです。以下

(. 
    #object[org.glassfish.jersey.client.JerseyWebTarget 0x107a5073 "[email protected]"] 
    queryParam 
    "key1" 
    #object["[Ljava.lang.Object;" 0x16751ba2 "[Ljava.lang.Object;@16751ba2"]) 

に展開

(defmacro test-macro 
    [] 
    (list 
    (symbol ".queryParam") 
    (-> (ClientBuilder/newClient) 
     (.target "https://www.test.com")) 
    "key1" 
    (object-array ["val1"]))) 

望ましい結果は次のとおりです。

(.queryParam 
    #object[org.glassfish.jersey.client.JerseyWebTarget 0x107a5073 "[email protected]"]  
    "key1" 
    #object["[Ljava.lang.Object;" 0x16751ba2 "[Ljava.lang.Object;@16751ba2"]) 

私は.が評価され、周りに移動し得るために何かを引き起こしていると思いますか?その場合、解決策はそれを引用することになります。しかし、評価された式の結果をどのように引用できますか?

私の目標は、mapキーを呼び出す関数にして値をJava関数に渡す引数にすることによって、マップをオブジェクトを構築するコードに変換することです。


私はスレッドとdo-toマクロの使い方を理解していますが、機能構築のためにリクエストデータを作成しようとしています。私は "queryParam"としてキーと引数として値を持つマップを取ることができるようにしたい。これを持っていることで、Javaクラス全体で1つの関数を書くだけで十分で、1対1のマッピングで十分ですが、他の人がそれを魔法に使うとは思わないでしょう。

(def test-map {"target" ["https://www.test.com"] 
       "path" ["qa" "rest/service"] 
       "queryParam" [["key1" (object-array ["val1"])] 
           ["key2" (object-array ["val21" "val22" "val23"])]] }) 

(-> (ClientBuilder/newClient) 
    (.target "https://www.test.com") 
    (.path "qa") 
    (.path "rest/service") 
    (.queryParam "key1" (object-array ["val1"])) 
    (.queryParam "key2" (object-array ["val21" "val22" "val23"]))) 

答えて

2

あなたの質問から、ビルダーのデータ構造としてマップを使用する必要があるかどうかは不明です。あなたはdotoマクロを使用することができますBuilderパターンを実装し、その方法はvoid(例えばsetterメソッド)を返さないクラスについて

(-> (ClientBuilder.) 
     (.forEndpoint "http://example.com") 
     (.withQueryParam "key1" "value1") 
     (.build)) 

:私は、Builderパターンを実装するJavaクラスを直接操作するためのスレッドのマクロを使用することをお勧めします:

(doto (Client.) 
    (.setEndpoint "http://example.com") 
    (.setQueryParam "key1" "value1")) 

Javaメソッド呼び出しをエンコードするためのマップを使用してマクロを実装することは可能ですが、扱いにくいことです。複数のパラメータを持つメソッドを呼び出すことができるように、シーケンス内に各メソッド引数を保持する必要があります。また、メソッド呼び出しを指定するためにmapを使用して、varargを処理する単一パラメータメソッドの引数を格納するための規則を持ちます。それらが呼び出される順序を保証します。それは、あなたのコードに多くの複雑さと魔法を追加します。

これは、あなたがそれを実装する方法です:

(defmacro builder [b m] 
    (let [method-calls 
     (map (fn [[k v]] `(. (~(symbol k) [email protected]))) m)] 
    `(-> ~b 
     [email protected]))) 

(macroexpand-1 
    '(builder (StringBuilder.) {"append" ["a"]})) 
;; => (clojure.core/-> (StringBuilder.) (. (append "a"))) 

(str 
    (builder (StringBuilder.) {"append" ["a"] })) 
;; => "a"