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

Java雜談之如何優(yōu)化寫出漂亮高效的代碼

 更新時(shí)間:2021年09月30日 16:57:01   作者:JavaEdge.  
不一致的代碼會(huì)造成認(rèn)知上的負(fù)擔(dān),在一個(gè)系統(tǒng)中,做類似的事情,卻有不同的做法,或者起到類似作用的事物,卻有不同的名字,讓人困惑

大部分程序員對(duì)于一致性本身的重要性是有認(rèn)知的。但通常來(lái)說(shuō),大家理解的一致性都表現(xiàn)在比較大的方面,比如,數(shù)據(jù)庫(kù)訪問(wèn)是叫 DAO還是叫 Mapper,Repository?在一個(gè)團(tuán)隊(duì)內(nèi),這是有統(tǒng)一標(biāo)準(zhǔn)的,但編碼的層面上,要求往往就不是那么細(xì)致了。所以,我們才會(huì)看到在代碼細(xì)節(jié)上呈現(xiàn)出了各種不一致。我們還是從一段具體的代碼來(lái)分析問(wèn)題。

命名中的不一致

有一次,我在代碼評(píng)審中看到了這樣一段代碼:

enum DistributionChannel {
  ​WEBSITE
  ​KINDLE_ONLY
  ​AL
}

使用標(biāo)記作品的分發(fā)渠道,從這段代碼的內(nèi)容上,我們可以看到,目前的分發(fā)渠道包括:

  • 網(wǎng)站(WEBSITE)
  • 只在Kindle(KINDLE_ONLY)
  • 全渠道(ALL)

面對(duì)這段代碼,我有些疑惑,于是我提了一個(gè)問(wèn)題:

  • WEBSITE 和 KINDLE_ONLY 分別表示的是什么?

WEBSITE 表示作品只會(huì)在我們自己的網(wǎng)站發(fā)布,KINDLE_ONLY 表示這部作品只會(huì)在 Kindle 的電子書商店里上架。

  • 二者是不是都表示只在單獨(dú)一個(gè)渠道發(fā)布?

是??!

  • 既然二者都有只在一個(gè)平臺(tái)上架發(fā)布的含義,為什么不都叫 XXX 或者 XXX_ONLY?

呃,你說(shuō)得有道理。

問(wèn)題原因就是這里 WEBSITE 和 KINDLE_ONLY 兩個(gè)名字不一致。

表示類似含義的代碼應(yīng)該有一致的名字,比如,很多團(tuán)隊(duì)里都會(huì)把業(yè)務(wù)寫到服務(wù)層,各種服務(wù)的命名也通常都是 XXXService。
一旦出現(xiàn)不一致名字,通常都表示不同含義。比如,對(duì)于那些非業(yè)務(wù)入口的業(yè)務(wù)組件,它們的名字就會(huì)不一樣,會(huì)更符合其具體業(yè)務(wù)行為,像BookSender ,它表示將作品發(fā)送到翻譯引擎。

一般枚舉值表示的含義應(yīng)該都有一致的業(yè)務(wù)含義,一旦出現(xiàn)不同,我就需要確定不同的點(diǎn)到底在哪里,這就是我提問(wèn)的緣由。

顯然,這段代碼的作者給這兩個(gè)枚舉值命名時(shí),只是分別考慮了它應(yīng)該起什么名字,卻忽略了這個(gè)枚舉值在整體中扮演的角色。

理解這一點(diǎn),改動(dòng)是很容易,后來(lái),代碼被統(tǒng)一成了一個(gè)形式:

enum DistributionChannel {
  ​WEBSITE
  ​KINDLE
  ​AL
}

方案中的不一致

// 生成時(shí)間戳
public String nowTimestamp() {
  DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  Date now = new Date();
  return format.format(now);
}

當(dāng)一個(gè)系統(tǒng)向另外一個(gè)系統(tǒng)發(fā)送請(qǐng)求時(shí),需要帶一個(gè)時(shí)間戳過(guò)去,這里就是把這個(gè)時(shí)間戳按照一定格式轉(zhuǎn)成了字符串類型,主要就是傳輸用,便于另外的系統(tǒng)進(jìn)行識(shí)別,也方便在開(kāi)發(fā)過(guò)程中進(jìn)行調(diào)試。

這段代碼本身的實(shí)現(xiàn)是沒(méi)有問(wèn)題的。它甚至考慮到了 SimpleDateFormat 這個(gè)類本身存在的多線程問(wèn)題,所以,它每次去創(chuàng)建了一個(gè)新的 SimpleDateFormat 對(duì)象。

那我為什么還說(shuō)它是有問(wèn)題的呢?因?yàn)檫@種寫法是 Java 8 之前的寫法,而我們用的 Java 版本是 Java 8 之后的。

在很長(zhǎng)的一段時(shí)間里,Java 的日期時(shí)間解決方案一直是一個(gè)備受爭(zhēng)議的設(shè)計(jì),它的問(wèn)題很多,有的是概念容易讓人混淆(比如:Date 和 Calendar 什么情況下該用哪個(gè)),有的是接口設(shè)計(jì)的不直觀(比如:Date 的 setMonth 參數(shù)是從 0 到 11),有的是實(shí)現(xiàn)容易造成問(wèn)題(比如:前面提到的 SimpleDateFormat 需要考慮多線程并發(fā)的問(wèn)題,需要每次構(gòu)建一個(gè)新的對(duì)象出來(lái))。

這種亂象存在了很長(zhǎng)時(shí)間,有很多人都在嘗試解決這個(gè)問(wèn)題(比如 Joda Time)。從 Java 8開(kāi)始,Java 官方的 SDK 借鑒了各種程序庫(kù),引入了全新的日期時(shí)間解決方案。這套解決方案與原有的解決方案是完全獨(dú)立的,也就是說(shuō),使用這套全新的解決方案完全可以應(yīng)對(duì)我們的所有工作。

我們現(xiàn)在的這個(gè)項(xiàng)目是一個(gè)全新的項(xiàng)目,我們使用的版本是 Java 11,這就意味著我們完全可以使用這套從 Java 8 引入的日期時(shí)間解決方案。所以,我們?cè)陧?xiàng)目里的約定就是所有的日期時(shí)間類型就是使用這套新的解決方案。

現(xiàn)在你可能已經(jīng)知道我說(shuō)的問(wèn)題在哪里了,在這個(gè)項(xiàng)目里,我們的要求是使用新的日期時(shí)間解決方案,而這里的 SimpleDateFormat 和 Date 是舊解決方案的一部分。所以,雖然這段代碼本身的實(shí)現(xiàn)是沒(méi)有問(wèn)題的,然而,放在項(xiàng)目整體中,這卻是一個(gè)壞味道,因?yàn)樗鼪](méi)有和其它的部分保持一致。

后來(lái)使用了新的解決方案:

public String nowTimestamp() {
  ​LocalDateTime now = LocalDateTime.now()
  return now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}

之所以會(huì)這樣,因?yàn)橐粋€(gè)項(xiàng)目中,應(yīng)對(duì)同一個(gè)問(wèn)題出現(xiàn)了多個(gè)解決方案,如果沒(méi)有統(tǒng)一約定,項(xiàng)目成員會(huì)根據(jù)自己寫代碼時(shí)的感覺(jué)隨機(jī)選擇方案,導(dǎo)致方案不一致。

為什么一個(gè)項(xiàng)目中會(huì)出現(xiàn)多個(gè)解決方案?

  • 時(shí)間

時(shí)間消逝,技術(shù)發(fā)展,人們會(huì)主動(dòng)意識(shí)到原方案的問(wèn)題,就會(huì)提出新方案,像這里 Java 日期時(shí)間的解決方案,就是 JDK 本身隨時(shí)間演化造成的。有的項(xiàng)目時(shí)間比較長(zhǎng),也會(huì)出現(xiàn)類似問(wèn)題。

  • 因?yàn)樽约旱脑蛞?br />

比如,在代碼中引入做同一件事情類似的程序庫(kù)。比如判斷字符串是否為空或空串,就有 Guava 和 Apache Commons Lang,都能做同樣事情,所以,程序員也會(huì)根據(jù)自己的熟悉程度選擇其中之一來(lái)用,造成代碼不一致。

這兩個(gè)程序庫(kù)是很多程序庫(kù)的基礎(chǔ),經(jīng)常因?yàn)橐肓似渌绦驇?kù),相應(yīng)的依賴就出現(xiàn)在我們的代碼中。所以,我們必須約定,哪種做法是我們?cè)陧?xiàng)目中的標(biāo)準(zhǔn)做法,以防出現(xiàn)各自為戰(zhàn)的現(xiàn)象。比如,在我的團(tuán)隊(duì)中,我們就選擇 Guava 作為基礎(chǔ)庫(kù),因?yàn)橄鄬?duì)來(lái)說(shuō),它的風(fēng)格更現(xiàn)代,所以,團(tuán)隊(duì)就約定類似的操作都以 Guava 為準(zhǔn)。

代碼中的不一致

public void createBook(final List<BookId> bookIds) throws IOException {
  ​List<Book> books = bookService.getApprovedBook(bookIds)
  ​CreateBookParameter parameter = toCreateBookParameter(books)
  ​HttpPost post = createBookHttpRequest(parameter)
  ​httpClient.execute(post)
}

在翻譯引擎中創(chuàng)建作品的代碼:

  • 首先,根據(jù)要處理的作品 ID,獲取其中已審核通過(guò)的作品
  • 然后,發(fā)送一個(gè) HTTP 請(qǐng)求在翻譯引擎中創(chuàng)建出這個(gè)作品

有什么問(wèn)題?
這段代碼的不一致,這些代碼不是一個(gè)層次的代碼!

首先是獲取審核通過(guò)的作品,這是一個(gè)業(yè)務(wù)動(dòng)作,接下來(lái)的三行其實(shí)是在做一件事,也就是發(fā)送創(chuàng)建作品的請(qǐng)求,這三行代碼:

  • 創(chuàng)建請(qǐng)求的參數(shù)
  • 根據(jù)參數(shù)創(chuàng)建請(qǐng)求
  • 最后把請(qǐng)求發(fā)送出去

三行代碼合起來(lái)完成了一個(gè)發(fā)送創(chuàng)建作品請(qǐng)求這么一件事,而這件事才是一個(gè)完整的業(yè)務(wù)動(dòng)作。

所以,這個(gè)函數(shù)里的代碼并不在一個(gè)層次上,有的是業(yè)務(wù)動(dòng)作,有的是業(yè)務(wù)動(dòng)作的細(xì)節(jié)。理解到這,把這些業(yè)務(wù)細(xì)節(jié)的代碼提取到一個(gè)函數(shù):

public void createBook(final List<BookId> bookIds) throws IOException {
  ​List<Book> books = bookService.getApprovedBook(bookIds)
  ​createRemoteBook(books)
}

private void createRemoteBook(List<Book> books) throws IOException {
  ​CreateBookParameter parameter = toCreateBookParameter(books)
  ​HttpPost post = createBookHttpRequest(parameter)
  ​httpClient.execute(post)
}

結(jié)果上看,原來(lái)的函數(shù)(createBook)里都是業(yè)務(wù)動(dòng)作,而提取出來(lái)的函數(shù)(createRemoteBook)則都是業(yè)務(wù)動(dòng)作的細(xì)節(jié),各自語(yǔ)句都在一個(gè)層次。

分清代碼處于不同層次,基本功還是分離關(guān)注點(diǎn)!

一旦分解出不同關(guān)注點(diǎn),還可進(jìn)一步調(diào)整代碼的結(jié)構(gòu)。
像前面拆分出來(lái)的這個(gè)方法,我們已經(jīng)知道它的作用是發(fā)出一個(gè)請(qǐng)求去創(chuàng)建作品,本質(zhì)上并不屬于這個(gè)業(yè)務(wù)類的一部分。
所以,還可通過(guò)引入一個(gè)新模型,將這個(gè)部分調(diào)整出去:

public void createBook(final List<BookId> bookIds) throws IOException {
  List<Book> books = this.bookService.getApprovedBook(bookIds);
  this.translationEngine.createBook(books);
}


class TranslationEngine {
  public void createBook(List<Book> books) throws IOException {
    ​CreateBookParameter parameter = toCreateBookParameter(books)
    ​HttpPost post = createBookHttpRequest(parameter)
    ​httpClient.execute(post)
  ​
  ​..
}

一說(shuō)到分層,大多數(shù)人想到的只是模型的分層,很少有人會(huì)想到在函數(shù)的語(yǔ)句中也要分層。各種層次的代碼混在一起,許多問(wèn)題也就隨之而來(lái)了,最典型莫過(guò)長(zhǎng)函數(shù)。

我們?cè)谧龅囊廊皇悄P头謱?,只不過(guò),這次的出發(fā)點(diǎn)是函數(shù)的語(yǔ)句?!胺蛛x關(guān)注點(diǎn),越小越好”的意義所在。觀察代碼的粒度足夠小,很多問(wèn)題自然就會(huì)暴露出來(lái)。

程序員開(kāi)始寫測(cè)試時(shí),有一個(gè)典型的問(wèn)題:如何測(cè)試一個(gè)私有方法。有人建議用一些特殊能力(比如反射)去測(cè)試。我給這個(gè)問(wèn)題的答案是,不要測(cè)私有方法。
之所以想測(cè)試私有方法,就是分離關(guān)注點(diǎn)沒(méi)有做好,把不同層次的代碼混在一起。前面這段代碼,如果要測(cè)試前面那個(gè) createRemoteBook 方法還是有一定難度的,但調(diào)整之后,引入了 TranslationEngine 這個(gè)類,這個(gè)方法就變成了一個(gè)公開(kāi)方法,就可以按照一個(gè)公開(kāi)方法去測(cè)試了,所有問(wèn)題迎刃而解。

很多程序員糾結(jié)的技術(shù)問(wèn)題,其實(shí)是一個(gè)軟件設(shè)計(jì)問(wèn)題,不要通過(guò)奇技淫巧去解決一個(gè)本來(lái)不應(yīng)該被解決的問(wèn)題。

總結(jié)

對(duì)于一個(gè)團(tuán)隊(duì)來(lái)說(shuō),一致是非常重要的,是降低集體認(rèn)知成本的重要方式。我們分別見(jiàn)識(shí)了:

  • 命名中的不一致
  • 方案中的不一致
  • 代碼中的不一致。

類似含義的代碼應(yīng)該有類似的命名,不一致的命名表示不同含義,需要給出一個(gè)有效解釋。

方案中的不一致:

  • 由于代碼長(zhǎng)期演化造成的
  • 項(xiàng)目中存在完成同樣功能的程序庫(kù)

無(wú)論是哪種原因,都需要團(tuán)隊(duì)先統(tǒng)一約定,保證所有人按照同一種方式編寫代碼。

代碼中的不一致常常是把不同層次的代碼寫在了一起,最典型的就是把業(yè)務(wù)層面的代碼和實(shí)現(xiàn)細(xì)節(jié)的代碼混在一起。解決這種問(wèn)題的方式,就是通過(guò)提取方法,把不同層次的代碼放到不同的函數(shù)里,而這一切的前提還是是分離關(guān)注點(diǎn),這個(gè)代碼問(wèn)題的背后還是設(shè)計(jì)問(wèn)題。

保持代碼在各個(gè)層面上的一致性。

到此這篇關(guān)于Java雜談之如何優(yōu)化寫出漂亮高效的代碼的文章就介紹到這了,更多相關(guān)Java 代碼優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實(shí)戰(zhàn)之實(shí)現(xiàn)物流配送系統(tǒng)示例詳解

    Java實(shí)戰(zhàn)之實(shí)現(xiàn)物流配送系統(tǒng)示例詳解

    這篇文章主要介紹了一個(gè)java實(shí)戰(zhàn)項(xiàng)目:通過(guò)java、SSM、JSP、mysql和redis實(shí)現(xiàn)一個(gè)物流配送系統(tǒng)。文中的示例代碼非常詳細(xì),需要的朋友可以參考一下
    2021-12-12
  • SpringBoot application.yml和bootstrap.yml的區(qū)別

    SpringBoot application.yml和bootstrap.yml的區(qū)別

    本文主要介紹了SpringBoot application.yml和bootstrap.yml的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Java函數(shù)式編程之通過(guò)行為參數(shù)化傳遞代碼

    Java函數(shù)式編程之通過(guò)行為參數(shù)化傳遞代碼

    行為參數(shù)化就是可以幫助你處理頻繁變更的需求的一種軟件開(kāi)發(fā)模式,這篇文章將給大家詳細(xì)的介紹一下Java函數(shù)式編程之行為參數(shù)化傳遞代碼,感興趣的同學(xué)可以參考閱讀下
    2023-08-08
  • 最新JVM垃圾回收算法詳解

    最新JVM垃圾回收算法詳解

    ? 垃圾收集器對(duì)堆進(jìn)行回收前,首先要確定堆中的對(duì)象哪些還"存活",哪些已經(jīng)"死去"。有兩種算法,分別是引用計(jì)數(shù)算法(Recference?Counting)和可達(dá)性分析算法(Reachability?Analysis),這篇文章主要介紹了JVM垃圾回收算法,需要的朋友可以參考下
    2022-05-05
  • Java String字符串和Unicode字符相互轉(zhuǎn)換代碼詳解

    Java String字符串和Unicode字符相互轉(zhuǎn)換代碼詳解

    這篇文章主要介紹了Java String字符串和Unicode字符相互轉(zhuǎn)換代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • Java數(shù)據(jù)結(jié)構(gòu)之棧與綜合計(jì)算器的實(shí)現(xiàn)

    Java數(shù)據(jù)結(jié)構(gòu)之棧與綜合計(jì)算器的實(shí)現(xiàn)

    這篇文章主要為大家詳細(xì)介紹了Java數(shù)據(jù)結(jié)構(gòu)中棧與綜合計(jì)算器的實(shí)現(xiàn),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下
    2022-10-10
  • 基于Jpa中ManyToMany和OneToMany的雙向控制

    基于Jpa中ManyToMany和OneToMany的雙向控制

    這篇文章主要介紹了Jpa中ManyToMany和OneToMany的雙向控制,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Springboot實(shí)現(xiàn)過(guò)濾器的兩種方式

    Springboot實(shí)現(xiàn)過(guò)濾器的兩種方式

    今天通過(guò)本文給大家分享Springboot實(shí)現(xiàn)過(guò)濾器的兩種方式,第一種是spring容器注冊(cè)filter,第二種方式是通過(guò)@WebFilter 注解來(lái)配置,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2023-10-10
  • Spring Boot Admin 進(jìn)行項(xiàng)目監(jiān)控管理的方法

    Spring Boot Admin 進(jìn)行項(xiàng)目監(jiān)控管理的方法

    Spring Boot Admin是一個(gè)開(kāi)源社區(qū)項(xiàng)目,用于管理和監(jiān)控SpringBoot應(yīng)用程序。 這篇文章主要介紹了 Spring Boot Admin 進(jìn)行項(xiàng)目監(jiān)控管理的方法,需要的朋友可以參考下
    2020-07-07
  • 使用SpringSecurity設(shè)置角色和權(quán)限的注意點(diǎn)

    使用SpringSecurity設(shè)置角色和權(quán)限的注意點(diǎn)

    這篇文章主要介紹了使用SpringSecurity設(shè)置角色和權(quán)限的注意點(diǎn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03

最新評(píng)論