欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Clojure?與Java對(duì)比少數(shù)據(jù)結(jié)構(gòu)多函數(shù)勝過(guò)多個(gè)單獨(dú)類的優(yōu)點(diǎn)

 更新時(shí)間:2022年06月10日 08:37:07   作者:?sofia??  
這篇文章主要介紹了Clojure?與Java對(duì)比少數(shù)據(jù)結(jié)構(gòu)多函數(shù)勝過(guò)多個(gè)單獨(dú)類的優(yōu)點(diǎn),在Clojure中,我們一次又一次地使用相同的數(shù)據(jù)結(jié)構(gòu),并在其上運(yùn)行許多函,更多相關(guān)介紹需要的朋友可以參考一下下面文章內(nèi)容

前言:

在Clojure中,我們一次又一次地使用相同的數(shù)據(jù)結(jié)構(gòu),并在其上運(yùn)行許多函數(shù)。另一方面,Java程序員為每一組數(shù)據(jù)創(chuàng)建一個(gè)唯一的類,并使用自己的“API”(getter、setter、return type等)來(lái)訪問(wèn)和操作數(shù)據(jù)。由于被迫在兩個(gè)這樣的“類API”之間進(jìn)行翻譯,我想與大家分享我的經(jīng)驗(yàn),從而在實(shí)踐中證明格言中的真理

請(qǐng)注意,本文談?wù)摰氖菙?shù)據(jù)和數(shù)據(jù)承載類,而不是“業(yè)務(wù)邏輯”,它將由Java中所述對(duì)象上的方法和Clojure中命名空間中的函數(shù)(最好是純函數(shù))實(shí)現(xiàn)。

注意:本文會(huì)交替使用Java和Groovy,因?yàn)樗鼈兓鞠嗤?;本文所說(shuō)的一個(gè)也適用于另一個(gè)。

問(wèn)題所在

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

這是一件痛苦的事,因?yàn)槊總€(gè)人都有自己的頭表示和使用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),但頭和請(qǐng)求對(duì)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)。但這是不可能的,我必須從一組類型映射到另一組類型。這里的主要問(wèn)題是servlet請(qǐng)求被拆分為getHeaderNamesgetHeaders,而不是返回例如Map<String,String[]>,還有RequestBuilder,它有addHeader,但無(wú)法一次添加所有頭(除非我們首先將它們包裝在其域類中,即Header中)。

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

Clojure解決方案

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

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

案例1:相同的Keys

最簡(jiǎn)單的情況是,使用相同的key,我們只想選擇一個(gè)子集:

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

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

案例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)鍵是,在從一個(gè)數(shù)據(jù)結(jié)構(gòu)映射到另一個(gè)數(shù)據(jù)結(jié)構(gòu)的過(guò)程中,我們?nèi)匀皇褂梦覀兯篮拖矏?ài)的相同功能,唯一針對(duì)具體情況的部分是鍵和鍵轉(zhuǎn)換函數(shù)。我們可以簡(jiǎn)單地映射頭映射,這在HttpServletRequest的頭上是不可能的。

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

headers作為name-value對(duì)列表(可能有重復(fù)的名稱)進(jìn)入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

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

請(qǐng)求如下所示:

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

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

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

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

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

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

這就是上面第1種情況。

Java Vs Clojure

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

選擇

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

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

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

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

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

轉(zhuǎn)換

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

// fake code <img src="https://javakk.com/wp-content/themes/Tint-master/images/smilies/icon_smile.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是不必要的,因?yàn)槲覀冎挥杏成?,沒(méi)有要從/映射到的類型。我們?cè)谶@里的前提是,數(shù)據(jù)的“形狀”在兩端都是相同的,但即使不是,也更容易從一個(gè)轉(zhuǎn)換到另一個(gè),因?yàn)閿?shù)據(jù)轉(zhuǎn)換是FP的主要優(yōu)勢(shì)之一,尤其是Clojure。核心庫(kù)中有許多有用的數(shù)據(jù)選擇和轉(zhuǎn)換功能,旨在以多種強(qiáng)大的方式進(jìn)行組合。

驗(yàn)證、封裝?

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

結(jié)論

Clojure在任何地方都使用相同的少數(shù)數(shù)據(jù)結(jié)構(gòu)(map、set、list、vector),并具有許多操作這些結(jié)構(gòu)的函數(shù)(許多函數(shù)如map on all,一些函數(shù)如select key only on some)。最終,您將非常熟練地使用這些功能以及將它們結(jié)合起來(lái)以實(shí)現(xiàn)您想要的任何功能的方法。

Java開(kāi)發(fā)人員必須為每個(gè)類學(xué)習(xí)一個(gè)新的“數(shù)據(jù)訪問(wèn)API”,并進(jìn)行大量的手動(dòng)翻譯。她在一節(jié)課上學(xué)到的東西在另一節(jié)課上通常是無(wú)用的。

Clojure方法似乎更有成效。但它超越了開(kāi)發(fā)人員的生產(chǎn)力。所有Clojure庫(kù)都使用相同的少數(shù)通用數(shù)據(jù)結(jié)構(gòu),因此可以編寫同樣通用的實(shí)用程序庫(kù)來(lái)處理數(shù)據(jù),如Specter或Balagan,這些數(shù)據(jù)可以用于Ring請(qǐng)求、Hiccup HTML表示、“來(lái)自后端服務(wù)的json”數(shù)據(jù)以及其他任何數(shù)據(jù)。

到此這篇關(guān)于Clojure 與Java對(duì)比少數(shù)據(jù)結(jié)構(gòu)多函數(shù)勝過(guò)多個(gè)單獨(dú)類的優(yōu)點(diǎn)的文章就介紹到這了,更多相關(guān)Clojure 與 Java 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Scoket實(shí)現(xiàn)雙向通信代碼詳解

    Java Scoket實(shí)現(xiàn)雙向通信代碼詳解

    這篇文章主要介紹了Java Scoket實(shí)現(xiàn)雙向通信代碼詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • @Scheduled定時(shí)器原理及@RefreshScope相互影響

    @Scheduled定時(shí)器原理及@RefreshScope相互影響

    這篇文章主要為大家介紹了@Scheduled定時(shí)器原理及@RefreshScope相互影響詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 精通Java接口的使用與原理

    精通Java接口的使用與原理

    接口,在JAVA編程語(yǔ)言中是一個(gè)抽象類型,是抽象方法的集合,接口通常以interface來(lái)聲明。一個(gè)類通過(guò)繼承接口的方式,從而來(lái)繼承接口的抽象方法
    2022-03-03
  • 淺談Mybatis中resultType為hashmap的情況

    淺談Mybatis中resultType為hashmap的情況

    這篇文章主要介紹了淺談Mybatis中resultType為hashmap的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • synchronized底層實(shí)現(xiàn)原理

    synchronized底層實(shí)現(xiàn)原理

    這篇文章主要介紹了synchronized底層實(shí)現(xiàn)原理,想弄懂它的實(shí)現(xiàn)synchronized的原理,我們只能通過(guò)看編譯好的字節(jié)碼文件,下面文章的詳細(xì)內(nèi)容,我們就先從測(cè)試類開(kāi)始吧,需要的小伙伴可以參考一下
    2022-01-01
  • 淺談Java中ABA問(wèn)題及避免

    淺談Java中ABA問(wèn)題及避免

    這篇文章主要介紹了淺談Java中ABA問(wèn)題及避免,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • springboot+vue實(shí)現(xiàn)Minio文件存儲(chǔ)的示例代碼

    springboot+vue實(shí)現(xiàn)Minio文件存儲(chǔ)的示例代碼

    本文主要介紹了springboot+vue實(shí)現(xiàn)Minio文件存儲(chǔ)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • 詳解Spring AOP的原理與實(shí)現(xiàn)方式

    詳解Spring AOP的原理與實(shí)現(xiàn)方式

    Spring框架是一個(gè)功能強(qiáng)大且靈活的企業(yè)級(jí)應(yīng)用程序開(kāi)發(fā)框架,其中最重要的特性之一就是面向切面編程(AOP),我們今天這篇文章將從源碼和案例的角度詳細(xì)介紹Spring AOP的思想、原理和實(shí)現(xiàn)方式
    2023-07-07
  • 詳解spring cloud使用Hystrix實(shí)現(xiàn)單個(gè)方法的fallback

    詳解spring cloud使用Hystrix實(shí)現(xiàn)單個(gè)方法的fallback

    本篇文章主要介紹了詳解spring cloud-使用Hystrix實(shí)現(xiàn)單個(gè)方法的fallback,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • java設(shè)計(jì)模式之單例模式的詳解及優(yōu)點(diǎn)

    java設(shè)計(jì)模式之單例模式的詳解及優(yōu)點(diǎn)

    這篇文章主要介紹了java設(shè)計(jì)模式之單例模式的詳解及優(yōu)點(diǎn)的相關(guān)資料,如果一個(gè)類始終只能創(chuàng)建一個(gè)實(shí)例,那么這個(gè)類被稱為單例類,這種設(shè)計(jì)模式被稱為單例模式,需要的朋友可以參考下
    2017-08-08

最新評(píng)論