一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Java雜談之代碼重構(gòu)的方法多長(zhǎng)才算長(zhǎng)

Java雜談之代碼重構(gòu)的方法多長(zhǎng)才算長(zhǎng)

2022-01-26 12:47JavaEdge. Java教程

關(guān)于代碼重構(gòu)的理解:在不改變軟件系統(tǒng)/模塊所具備的功能特性的前提下,遵循/利用某種規(guī)則,使其內(nèi)部結(jié)構(gòu)趨于完善。其在軟件生命周期中的價(jià)值體現(xiàn)主要在于可維護(hù)性和可擴(kuò)展性

每當(dāng)看到長(zhǎng)函數(shù),我們都得:

  • 被迫理解一個(gè)長(zhǎng)函數(shù)
  • 在一個(gè)長(zhǎng)函數(shù)中,小心翼翼地找出需要的邏輯,按需求微調(diào)

幾乎所有程序員都會(huì)有類似經(jīng)歷。
沒人喜歡長(zhǎng)函數(shù),但你卻要一直和各種長(zhǎng)函數(shù)打交道。

幾百上千行的函數(shù)肯定是不足以稱霸的。

多長(zhǎng)算“長(zhǎng)”?

100 行?對(duì)于函數(shù)長(zhǎng)度容忍度太高了!這是導(dǎo)致長(zhǎng)函數(shù)產(chǎn)生的關(guān)鍵點(diǎn)。

看具體代碼時(shí),一定要能夠看到細(xì)微之處。關(guān)鍵點(diǎn)就是將任務(wù)拆解得越小越好,這個(gè)觀點(diǎn)對(duì)代碼同樣適用。隨著對(duì)代碼長(zhǎng)度容忍度的降低,對(duì)代碼細(xì)節(jié)的感知力就會(huì)逐漸提升,你才能看到那些原本所謂細(xì)枝末節(jié)的地方隱藏的各種問題。

“越小越好”是一個(gè)追求的目標(biāo),不過,沒有一個(gè)具體的數(shù)字,就沒辦法約束所有人的行為。所以,通常情況下,我們還是要定義出一個(gè)代碼行數(shù)的上限,以保證所有人都可以按照這個(gè)標(biāo)準(zhǔn)執(zhí)行。

像 Java 這樣表達(dá)能力稍弱的靜態(tài)類型語言,爭(zhēng)取 20 行代碼解決問題。

這不是一個(gè)說說就算的標(biāo)準(zhǔn),我們應(yīng)該把它變成一個(gè)可執(zhí)行的標(biāo)準(zhǔn)。比如,在 Java 中,我們就可以把代碼行的約束加到 CheckStyle 的配置文件:

?
1
2
3
4
5
<module name="MethodLength">
    <property name="tokens" value="METHOD_DEF"/>
    <property name="max" value="20"/>
    <property name="countEmpty" value="false"/>
</module>

這樣,在我們提交代碼之前,執(zhí)行本地的構(gòu)建腳本,就可以把長(zhǎng)函數(shù)檢測(cè)出來。

即便以 20 行上限,這也已經(jīng)超過很多人的認(rèn)知,具體的函數(shù)行數(shù)可以結(jié)合團(tuán)隊(duì)的實(shí)際情況來制定。
非常不建議把這個(gè)數(shù)字放得很大,就像我前面說的那樣,如果你放到 100 行,這個(gè)數(shù)字基本上是沒有太多意義的,對(duì)團(tuán)隊(duì)也起不到什么約束作用。

  • 如果函數(shù)里面的行寫得很長(zhǎng)呢?還應(yīng)不應(yīng)該插入換行?如果插入換行的話就會(huì)增加行數(shù),如果不差入換行,在看代碼時(shí)就要經(jīng)常移動(dòng)水平滾動(dòng)條,按代碼行而非物理行計(jì)數(shù)。

長(zhǎng)函數(shù)的產(chǎn)生

限制函數(shù)長(zhǎng)度,是一種簡(jiǎn)單粗暴的解決方案。最重要的是你要知道,長(zhǎng)函數(shù)本身是一個(gè)結(jié)果,如果不理解長(zhǎng)函數(shù)產(chǎn)生的原因,還是很難寫出整潔的代碼。

以性能為由

像 C 語言這種在今天已經(jīng)是高性能的程序設(shè)計(jì)語言,在問世之初,也曾被人質(zhì)疑性能不彰,尤其是函數(shù)調(diào)用。

在一些寫匯編語言的人看來,調(diào)用函數(shù)涉及到入棧出棧的過程,顯然不如直接執(zhí)行來得性能高。這種想法經(jīng)過各種演變流傳到今天,任何一門新語言出現(xiàn),還是會(huì)以同樣的理由被質(zhì)疑。

所以,在很多人看來,把函數(shù)寫長(zhǎng)是為了所謂性能。不過,這個(gè)觀點(diǎn)在今天是站不住的。性能優(yōu)化不該是寫代碼的第一考量:

  • 有活力的程序設(shè)計(jì)語言本身是不斷優(yōu)化的,無論是編譯器,還是運(yùn)行時(shí),性能都會(huì)越來越好
  • 可維護(hù)性比性能優(yōu)化要優(yōu)先考慮,當(dāng)性能不足以滿足需要時(shí),我們?cè)賮碜鱿鄳?yīng)的測(cè)量,找到焦點(diǎn),進(jìn)行特定的優(yōu)化。這比在寫代碼時(shí)就考慮所謂性能要更能鎖定焦點(diǎn),優(yōu)化才有意義。

平鋪直敘

寫代碼平鋪直敘,把自己想到的一點(diǎn)點(diǎn)羅列出來。比如下面這段代碼(如果你不想仔細(xì)閱讀,可以直接跳到后面):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public void executeTask() {
    ObjectMapper mapper = new ObjectMapper();
    CloseableHttpClient client = HttpClients.createDefault();
    List<Chapter> chapters = this.chapterService.getUntranslatedChapters();
    for (Chapter chapter : chapters) {
        // Send Chapter
        SendChapterRequest sendChapterRequest = new SendChapterRequest();
        sendChapterRequest.setTitle(chapter.getTitle());
        sendChapterRequest.setContent(chapter.getContent());
 
 
        HttpPost sendChapterPost = new HttpPost(sendChapterUrl);
        CloseableHttpResponse sendChapterHttpResponse = null;
        String chapterId = null;
        try {
            String sendChapterRequestText = mapper.writeValueAsString(sendChapterRequest);
            sendChapterPost.setEntity(new StringEntity(sendChapterRequestText));
            sendChapterHttpResponse = client.execute(sendChapterPost);
            HttpEntity sendChapterEntity = sendChapterPost.getEntity();
            SendChapterResponse sendChapterResponse = mapper.readValue(sendChapterEntity.getContent(), SendChapterResponse.class);
            chapterId = sendChapterResponse.getChapterId();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (sendChapterHttpResponse != null) {
                    sendChapterHttpResponse.close();
                }
            } catch (IOException e) {
                // ignore
            }
        }
 
 
        // Translate Chapter
        HttpPost translateChapterPost = new HttpPost(translateChapterUrl);
        CloseableHttpResponse translateChapterHttpResponse = null;
        try {
            TranslateChapterRequest translateChapterRequest = new TranslateChapterRequest();
            translateChapterRequest.setChapterId(chapterId);
            String translateChapterRequestText = mapper.writeValueAsString(translateChapterRequest);
            translateChapterPost.setEntity(new StringEntity(translateChapterRequestText));
            translateChapterHttpResponse = client.execute(translateChapterPost);
            HttpEntity translateChapterEntity = translateChapterHttpResponse.getEntity();
            TranslateChapterResponse translateChapterResponse = mapper.readValue(translateChapterEntity.getContent(), TranslateChapterResponse.class);
            if (!translateChapterResponse.isSuccess()) {
                logger.warn("Fail to start translate: {}", chapterId);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (translateChapterHttpResponse != null) {
                try {
                    translateChapterHttpResponse.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
    }

把沒有翻譯過的章節(jié)發(fā)到翻譯引擎,然后,啟動(dòng)翻譯過程。

翻譯引擎是另外一個(gè)服務(wù),需通過 HTTP 的形式向它發(fā)送請(qǐng)求。相對(duì)而言,這段代碼還算直白,當(dāng)你知道了我上面所說的邏輯,你是很容易看懂這段代碼。

這段代碼之所以很長(zhǎng),主要原因就是把前面所說的邏輯全部平鋪直敘地?cái)[在那里了,這里既有業(yè)務(wù)處理的邏輯,比如,把章節(jié)發(fā)送給翻譯引擎,然后,啟動(dòng)翻譯過程;又有處理的細(xì)節(jié),比如,把對(duì)象轉(zhuǎn)成 JSON,然后,通過 HTTP 客戶端發(fā)送出去。

從這段代碼中,可看到平鋪直敘的代碼存在的兩個(gè)典型問題:

  • 把多個(gè)業(yè)務(wù)處理流程放在一個(gè)函數(shù)里實(shí)現(xiàn)
  • 把不同層面的細(xì)節(jié)放到一個(gè)函數(shù)里實(shí)現(xiàn)

這里發(fā)送章節(jié)和啟動(dòng)翻譯是兩個(gè)過程,顯然,這是可以放到兩個(gè)不同的函數(shù)中去實(shí)現(xiàn)的,所以,我們只要做一下提取函數(shù),就可以把這個(gè)看似龐大的函數(shù)拆開,而拆出來的幾個(gè)函數(shù)規(guī)模都會(huì)小很多,像下面這樣:

?
1
2
3
4
5
6
7
8
9
public void executeTask() {
    ObjectMapper mapper = new ObjectMapper();
    CloseableHttpClient client = HttpClients.createDefault();
    List<Chapter> chapters = this.chapterService.getUntranslatedChapters();
    for (Chapter chapter : chapters) {
        String chapterId = sendChapter(mapper, client, chapter);
        translateChapter(mapper, client, chapterId);
    }
}

拆出來的部分,實(shí)際上就是把對(duì)象打包發(fā)送的過程,我們以發(fā)送章節(jié)為例,先來看拆出來的發(fā)送章節(jié)部分:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
private String sendChapter(final ObjectMapper mapper,
                           final CloseableHttpClient client,
                           final Chapter chapter) {
    SendChapterRequest request = asSendChapterRequest(chapter);
 
 
    CloseableHttpResponse response = null;
    String chapterId = null;
    try {
        HttpPost post = sendChapterRequest(mapper, request);
        response = client.execute(post);
        chapterId = asChapterId(mapper, post);
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        try {
            if (response != null) {
                response.close();
            }
        } catch (IOException e) {
            // ignore
        }
    }
    return chapterId;
}
 
 
private HttpPost sendChapterRequest(final ObjectMapper mapper, final SendChapterRequest sendChapterRequest) throws JsonProcessingException, UnsupportedEncodingException {
    HttpPost post = new HttpPost(sendChapterUrl);
    String requestText = mapper.writeValueAsString(sendChapterRequest);
    post.setEntity(new StringEntity(requestText));
    return post;
}
 
 
private String asChapterId(final ObjectMapper mapper, final HttpPost sendChapterPost) throws IOException {
    String chapterId;
    HttpEntity entity = sendChapterPost.getEntity();
    SendChapterResponse response = mapper.readValue(entity.getContent(), SendChapterResponse.class);
    chapterId = response.getChapterId();
    return chapterId;
}
 
 
private SendChapterRequest asSendChapterRequest(final Chapter chapter) {
    SendChapterRequest request = new SendChapterRequest();
    request.setTitle(chapter.getTitle());
    request.setContent(chapter.getContent());
    return request

這個(gè)代碼還算不上已經(jīng)處理得很整潔了,但至少同之前相比,已經(jīng)簡(jiǎn)潔了一些。我們只用了最簡(jiǎn)單的提取函數(shù)這個(gè)重構(gòu)手法,就把一個(gè)大函數(shù)拆分成了若干的小函數(shù)。

長(zhǎng)函數(shù)往往還隱含著一個(gè)命名問題。如果你看修改后的sendChapter,其中的變量命名明顯比之前要短,理解的成本也相應(yīng)地會(huì)降低。因?yàn)樽兞慷际窃谶@個(gè)短小的上下文里,也就不會(huì)產(chǎn)生那么多的命名沖突,變量名當(dāng)然就可以寫短一些。

平鋪直敘的代碼,一個(gè)關(guān)鍵點(diǎn)就是沒有把不同的東西分解出來。如果我們用設(shè)計(jì)的眼光衡量這段代碼,這就是“分離關(guān)注點(diǎn)”沒有做好,把不同層面的東西混在了一起,既有不同業(yè)務(wù)混在一起,也有不同層次的處理混在了一起。我在《軟件設(shè)計(jì)之美》專欄中,也曾說過,關(guān)注點(diǎn)越多越好,粒度越小越好。

一次加一點(diǎn)

有時(shí),一段代碼一開始的時(shí)候并不長(zhǎng),就像下面這段代碼,它根據(jù)返回的錯(cuò)誤進(jìn)行相應(yīng)地錯(cuò)誤處理:

?
1
2
3
if (code == 400 || code == 401) {
  // 做一些錯(cuò)誤處理
}

然后,新的需求來了,增加了新的錯(cuò)誤碼,它就變成了這個(gè)樣子:

?
1
2
3
if (code == 400 || code == 401 || code == 402) {
  // 做一些錯(cuò)誤處理
}

這段代碼有很多次被修改的機(jī)會(huì),日積月累:

?
1
2
3
4
5
if (code == 400 || code == 401 || code == 402 || ...
  || code == 500 || ...
  || ...
  || code == 10000 || ...) {
}

后人看到就想罵人。任何代碼都經(jīng)不起這種無意識(shí)的累積,每個(gè)人都沒做錯(cuò),但最終的結(jié)果很糟糕。對(duì)抗這種逐漸糟糕腐壞的代碼,需要知道“童子軍軍規(guī)”:
讓營地比你來時(shí)更干凈。

Robert Martin 把它借鑒到了編程領(lǐng)域,我們應(yīng)該看看自己對(duì)于代碼的改動(dòng)是不是讓原有的代碼變得更糟糕了,如果是,那就改進(jìn)它。
但這一切的前提是,你要能看出自己的代碼是不是讓原有的代碼變得糟糕了,所以,學(xué)習(xí)代碼的壞味道還是很有必要的。

至此,我們看到了代碼變長(zhǎng)的幾種常見原因:

  • 以性能為由
  • 平鋪直敘
  • 一次加一點(diǎn)

代碼變長(zhǎng)根本是一個(gè)無意識(shí)的問題,寫代碼的人沒有覺得自己把代碼破壞了。但只要你認(rèn)識(shí)到長(zhǎng)函數(shù)是一個(gè)壞味道,后面的許多問題就自然而然地會(huì)被發(fā)掘出來,至于解決方案,你已經(jīng)看到了,大部分情況下,就是拆分成各種小函數(shù)。

總結(jié)

沒有人愿意去閱讀長(zhǎng)函數(shù),但許多人又會(huì)不經(jīng)意間寫出長(zhǎng)函數(shù)。

對(duì)于團(tuán)隊(duì),一個(gè)關(guān)鍵點(diǎn)是要定義出長(zhǎng)函數(shù)的標(biāo)準(zhǔn)。
過于寬泛的標(biāo)準(zhǔn)沒有意義,想要有效地控制函數(shù)規(guī)模,幾十行已經(jīng)是標(biāo)準(zhǔn)上限,這個(gè)標(biāo)準(zhǔn)越低越好。

長(zhǎng)函數(shù)產(chǎn)生的原因:

  • 性能為借口
  • 代碼平鋪直敘

函數(shù)寫長(zhǎng)最常見的原因。之所以會(huì)把代碼平攤在那里:
- 把多個(gè)業(yè)務(wù)寫到了一起
- 把不同層次的代碼寫到了一起。究其根因,那是“分離關(guān)注點(diǎn)”沒有做好

  • 每人每次加一點(diǎn)點(diǎn)

應(yīng)對(duì)主要辦法就是要堅(jiān)守“童子軍軍規(guī)”,但其背后更深層次的支撐就是要對(duì)壞味道有著深刻的認(rèn)識(shí)

把函數(shù)寫短,越短越好。

到此這篇關(guān)于Java雜談之代碼重構(gòu)的方法多長(zhǎng)才算長(zhǎng)的文章就介紹到這了,更多相關(guān)Java 代碼重構(gòu)內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://blog.csdn.net/qq_33589510/article/details/120438225

延伸 · 閱讀

精彩推薦
  • Java教程Java8中Stream使用的一個(gè)注意事項(xiàng)

    Java8中Stream使用的一個(gè)注意事項(xiàng)

    最近在工作中發(fā)現(xiàn)了對(duì)于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個(gè)非常重要的注意點(diǎn),所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7482021-02-04
  • Java教程Java實(shí)現(xiàn)搶紅包功能

    Java實(shí)現(xiàn)搶紅包功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)搶紅包功能,采用多線程模擬多人同時(shí)搶紅包,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程20個(gè)非常實(shí)用的Java程序代碼片段

    20個(gè)非常實(shí)用的Java程序代碼片段

    這篇文章主要為大家分享了20個(gè)非常實(shí)用的Java程序片段,對(duì)java開發(fā)項(xiàng)目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程xml與Java對(duì)象的轉(zhuǎn)換詳解

    xml與Java對(duì)象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對(duì)象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
  • Java教程升級(jí)IDEA后Lombok不能使用的解決方法

    升級(jí)IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級(jí),尋思已經(jīng)有好久沒有升過級(jí)了。升級(jí)完畢重啟之后,突然發(fā)現(xiàn)好多錯(cuò)誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧...

    富貴穩(wěn)中求8032021-07-12
主站蜘蛛池模板: 艹逼的视频 | 国产精品理论片 | 国产精品视频二区不卡 | 石原莉奈adn093店长未婚妻 | 国产一级特黄在线播放 | 免费岛国片| 国产一区二区精品久久91 | 操到翻白眼 | 四虎新网址 | 男女一级簧色带 | 97精品国产高清在线看入口 | 亚洲成色www久久网站 | 暖暖视频免费观看视频中国.韩剧 | 黑人干亚洲人 | 日本福利视频网站 | 精品国产区一区二区三区在线观看 | 精品国产欧美一区二区三区成人 | 欧洲网色偷偷亚洲男人的天堂 | 四虎www.| 午夜大片在线观看 | 久久无码AV亚洲精品色午夜麻豆 | 国产免费小视频 | 久久xxxx| 精品久久久久久午夜 | 久久aa毛片免费播放嗯啊 | 2021最新国产成人精品视频 | 俄罗斯一级在线播放 | 天堂资源在线8 | 精品一区二区三区五区六区 | 免费在线观看网址大全 | 大香焦在线观看 | 精品日韩欧美一区二区三区 | 暖暖日本在线观看免费 | ass极品美妇pic | pppd在线播放 | 日本孕妇大胆孕交 | 7777奇米影视 | 娇妻被朋友征服中文字幕 | 成人一区二区免费中文字幕 | 精品久久久久久亚洲 | 日韩国产欧美视频 |