溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Clojure與Java對比實例分析

發(fā)布時間:2022-06-10 09:26:10 來源:億速云 閱讀:115 作者:zzz 欄目:開發(fā)技術(shù)

這篇文章主要介紹“Clojure與Java對比實例分析”,在日常操作中,相信很多人在Clojure與Java對比實例分析問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Clojure與Java對比實例分析”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

    問題所在

    我一直在寫一個代理,接收javax.servlet.http.HttpServletRequest 并通過Apache HttpClientorg.apache.http.client.methods.HttpUriRequest,然后從org.apache.http.HttpResponsejavax.servlet.http.HttpServletResponse,尤其是關(guān)于(一個子集)頭的響應(yīng)。

    這是一件痛苦的事,因為每個人都有自己的頭表示和使用headers的API:

    // javax.servlet.http.HttpServletRequest:
    Enumeration<String> getHeaderNames();
    /** Returns all the values of the specified request
        header as an Enumeration of String objects. */
    Enumeration<String> getHeaders(String name);
    
    // org.apache.http.client.methods.RequestBuilder:
    /** Add a header; repeat to add multiple values */
    RequestBuilder addHeader(String name, String value);
    
    //-------------
    // javax.servlet.http.HttpServletResponse:
    /** Add a header; repeat to add multiple values */
    void addHeader(String name, String value);
    
    // org.apache.http.HttpResponse:
    Header[] getAllHeaders();
    // Header:
    String getName();
    String getValue();

    這里,枚舉和數(shù)組是通用的數(shù)據(jù)結(jié)構(gòu),但頭和請求對getHeaderNamesgetHeaders的拆分需要特定的代碼。

    因此,我必須編寫translation函數(shù),如:

    def copyRequestHeaders(HttpServletRequest source, RequestBuilder target) {
        source.getHeaderNames().each { String hdr ->
            source.getHeaders(hdr).each { String val ->
                if (undesirable(hdr)) return
                target.addHeader(hdr, val)
            }
        }
    }

    static void copyResponseHeaders(HttpResponse source, HttpServletResponse target) {
        source.allHeaders.each { Header hdr ->
            if (target.getHeader(hdr.name.toLowerCase()) == hdr.value) return // avoid duplicates
            if (undesirable(hdr.name)) return
            target.addHeader(hdr.name, hdr.value)
        }
    }

    理想情況下,我希望能夠像target這樣做target.request.headers = omitKeys(undesirable, source.request.headers)。但這是不可能的,我必須從一組類型映射到另一組類型。這里的主要問題是servlet請求被拆分為getHeaderNamesgetHeaders,而不是返回例如Map<String,String[]>,還有RequestBuilder,它有addHeader,但無法一次添加所有頭(除非我們首先將它們包裝在其域類中,即Header中)。

    (可以說,我可以找到一個更好的例子來說明這一點。在這里,我們?nèi)匀恢饕ǖ豢偸牵┦褂妹杜e、字符串、數(shù)組等基元/泛型類型,而不是嵌套的自定義類型層次結(jié)構(gòu)。)

    Clojure解決方案

    在Clojure中,請求只是一個映射,標(biāo)題很可能是列表的映射。即使這兩個庫(服務(wù)器、客戶端)在密鑰名稱或數(shù)據(jù)結(jié)構(gòu)上不一致,也沒有“API”可學(xué)習(xí)-您只需使用相同的舊已知函數(shù)從一個數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換到另一個數(shù)據(jù)結(jié)構(gòu),這是您在每個Clojure項目、web、數(shù)據(jù)或任何其他領(lǐng)域中所做的事情。唯一改變的是地圖中關(guān)鍵點的名稱。

    注意:如果您不知道Clojure,那么一些示例可能很難閱讀,例如assocreduce-kv (key-value)函數(shù)以及偶爾的單字母名稱。請記住,Clojure程序員反復(fù)使用相同的100個函數(shù),并且非常熟悉它們。與其他一些語言相反,Clojure有意識地選擇為有經(jīng)驗的開發(fā)人員進行優(yōu)化。這對我來說很好。

    案例1:相同的Keys

    最簡單的情況是,使用相同的key,我們只想選擇一個子集:

    (assoc
      target-request
      :headers
      (select-keys (:headers source-request) [:pragma :content-type ...]))

    唯一區(qū)分大小寫的部分是keys。在Java中,您不能像我們在這里使用通用選擇鍵那樣一次選擇所有所需的keys,您需要通過類特定的getHeaders(name)逐個選擇它們。

    案例2:不同的Key名,相同的數(shù)據(jù)結(jié)構(gòu)

    (assoc
      target-request
      :headersX
      (clojure.set/rename-keys
        (select-keys (:headersY source-request) [:Pragma :ContentType ...])
        {:Pragma :pragma, :ContentType :content-type}))

    如果需要更復(fù)雜的key轉(zhuǎn)換,我們可以使用例如map:

    (defn transform-key [k] ...)
    (let [hdrs (->> (select-keys headers [:a])
                    (map (fn [[k v]] [(transform-key k) v]))
                    (into {}))]
        (assoc target-request :headersX hdrs))

    關(guān)鍵是,在從一個數(shù)據(jù)結(jié)構(gòu)映射到另一個數(shù)據(jù)結(jié)構(gòu)的過程中,我們?nèi)匀皇褂梦覀兯篮拖矏鄣南嗤δ埽ㄒ会槍唧w情況的部分是鍵和鍵轉(zhuǎn)換函數(shù)。我們可以簡單地映射頭映射,這在HttpServletRequest的頭上是不可能的。

    案例3:不同的數(shù)據(jù)結(jié)構(gòu)

    headers作為name-value對列表(可能有重復(fù)的名稱)進入name-value映射:

    (def headers-in [["pragma" "no-cache"] ["accept" "X"] ["accept" "Y"]])
    (->> headers-in
         (group-by first)
         (reduce-kv
           (fn [m k vs]
             (assoc
               m
               k
               (map second vs)))
           {}))
    ; => {"pragma" ("no-cache"), "accept" ("X" "Y")}

    案例4:Reality

    實際上,我們可能會使用Ring作為服務(wù)器,并將Clojure包裝器clj-http用于Apache HttpClient。

    請求如下所示:

    {:headers {"accept" "x,y", "pragma" "no-cache"}}

    (我們可以添加ring-request-headers-middleware,將連接的值轉(zhuǎn)換為單個值的列表。)

    Clj-http遵循Ring規(guī)范,因此支持相同的格式,但更為寬松:

    clj http對頭的處理比ring規(guī)范指定的要寬松一些。

    clj http允許任何大小寫的字符串或關(guān)鍵字,而不是強制所有請求頭都是小寫字符串。關(guān)鍵字將轉(zhuǎn)換為它們的規(guī)范表示形式,因此:content-md5標(biāo)頭將作為“content-md5”發(fā)送到服務(wù)器。但是,請求頭中的字符串鍵將被發(fā)送到服務(wù)器,其大小寫保持不變。

    響應(yīng)標(biāo)題可以作為任何大小寫的關(guān)鍵字或字符串讀取。如果服務(wù)器以“Date”標(biāo)頭響應(yīng),則可以訪問該標(biāo)頭的值,如:Date、“Date”、“Date”等。

    這就是上面第1種情況。

    Java Vs Clojure

    我想指出的一點是,Clojure在解決兩個問題方面更為有效:數(shù)據(jù)選擇和轉(zhuǎn)換,這要歸功于對其使用通用數(shù)據(jù)結(jié)構(gòu)和函數(shù)。

    選擇

    在Clojure中,通過選擇另一個映射的子集來創(chuàng)建映射非常簡單(assoc將鍵與值關(guān)聯(lián),select keys返回映射):

    (assoc
      request
      :headers
      (select-keys
        (:headers other-request)
        [:pragma ...]))

    使用典型的Java數(shù)據(jù)類(還記得DTOs嗎?)您需要逐個獲取和設(shè)置各個屬性。即使我們使用Groovy便利:

    new Person(
      firstName: employee.firstName,
      lastName: employee.lastName,
      ...)

    這里的重點并不是鍵入的數(shù)量,而是在Clojure中,我們可以使用現(xiàn)有函數(shù)(并將它們組合成新的可重用函數(shù))來完成這項工作,而在Java中,您必須編寫(更多)自定義的一次性代碼。(或者使用映射器庫、注釋和其他黑魔法:-))

    轉(zhuǎn)換

    如上所述,在Clojure中,將頭從一個請求復(fù)制到另一個請求是微不足道的。在典型的Java中,標(biāo)頭將由它們自己的類型(可能是標(biāo)頭)表示,因此,即使它們在兩個庫中具有相同的形狀,它們?nèi)匀皇遣煌念愋停?strong>我們需要從一種類型轉(zhuǎn)換為另一種類型:

    // fake code <img src="https://cache.yisu.com/upload/information/20220610/112/32986.gif" alt=":-)" />
    def toClientHdr(servlet.Header hdr) {
      return new httpclient.Header(
        name: hdr.name,
        values: hdr.values)
    }
    clientRequest.headers =
      servletRequest.headers
        .map(toClientHdr)

    在Clojure中,toClientHdr是不必要的,因為我們只有映射,沒有要從/映射到的類型。我們在這里的前提是,數(shù)據(jù)的“形狀”在兩端都是相同的,但即使不是,也更容易從一個轉(zhuǎn)換到另一個,因為數(shù)據(jù)轉(zhuǎn)換是FP的主要優(yōu)勢之一,尤其是Clojure。核心庫中有許多有用的數(shù)據(jù)選擇和轉(zhuǎn)換功能,旨在以多種強大的方式進行組合。

    驗證、封裝?

    即使您同意使用一些具有強大功能的通用數(shù)據(jù)結(jié)構(gòu)比將數(shù)據(jù)包裝在類型中更有效,您也可能會擔(dān)心類的其他好處,例如封裝和數(shù)據(jù)驗證。這超出了本文的范圍,但請確保FP/Clojure具有滿足這些需求的解決方案,盡管它們明顯不同于OOP。

    到此,關(guān)于“Clojure與Java對比實例分析”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

    向AI問一下細節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI