色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

由 Mybatis 源碼暢談軟件設計(九):“能用就行” 其實遠遠不夠

京東云 ? 來源:京東保險 王奕龍 ? 作者:京東保險 王奕龍 ? 2025-01-03 10:39 ? 次閱讀

作者:京東保險 王奕龍

到本節 Mybatis 源碼中核心邏輯基本已經介紹完了,在這里我想借助 Mybatis 其他部分源碼來介紹一些我認為在編程中能 最快提高編碼質量的小方法,它們可能比較細碎,希望能對大家有所啟發。

關于方法的長度和方法拆分

之前我在讀完《代碼整潔之道》時,非常癡迷于寫小方法這件事,它強調“每個方法只做一件事,方法的長度不能超過 5 行”等觀點。

記得某次代碼評審時,有同事對將一個大方法拆分成多個小方法提出了異議:拆分出的小方法不能算作做了一件事,它們都只是大方法中的一個“動作”而已,所以不應該拆分巴拉巴拉。

這個觀點讓我說不出什么,后來我也在想:如果按照這個觀點,多大的方法都可以概括成只做了一件事,那么我們就需要將所有的邏輯都“攤”到一個方法中嗎?我覺得拆分方法目的不是在界定一件事還是一個動作上,而是 關注方法的可讀性,拆分方法太多確實讓代碼變得不好讀,需要輾轉在多個方法之間,但是不拆的可讀性也會差,所以接下來我想根據 Mybatis 這段代碼來簡單談談我對寫方法的觀點:

public class XMLConfigBuilder extends BaseBuilder {

    private void parseConfiguration(XNode root) {
        try {
            propertiesElement(root.evalNode("properties"));
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            loadCustomVfsImpl(settings);
            loadCustomLogImpl(settings);
            typeAliasesElement(root.evalNode("typeAliases"));
            pluginsElement(root.evalNode("plugins"));
            objectFactoryElement(root.evalNode("objectFactory"));
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            settingsElement(settings);
            environmentsElement(root.evalNode("environments"));
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            typeHandlersElement(root.evalNode("typeHandlers"));
            mappersElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }

}

如上是 Mybatis 解析配置文件中各個標簽的方法,它將每個標簽的解析都單獨定義出了一個方法,這也是我一直遵循的寫方法的觀點:最頂層的入口方法應該是短小清晰的步驟,在主方法中編排好方法的執行內容,這樣主方法便是清晰明了的執行流程,我們便能一眼清晰的知道該方法做了什么事情,而針對各個具體的環節或者要改動哪些邏輯,直接跳轉到對應的方法即可。

至于該不該將某段邏輯抽象成一個方法,我的觀點是 能不能一眼看明白這段邏輯在干什么,如果不能,那么就應該被抽象到一個方法中,否則將其保留在原方法中也是沒有問題的對方法的抽象從來都不在于方法的長度可讀性 應得到更多的關注。

此外,還有一個能提高代碼可讀性的方法是: “合理使用換行符” ,如下代碼所示:

public class Configuration {
    // ...
    
    public Configuration() {
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

        typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
        typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
        typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

        typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
        typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
        typeAliasRegistry.registerAlias("LRU", LruCache.class);
        typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
        typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

        typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

        typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
        typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

        typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
        typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
        typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
        typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
        typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
        typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
        typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

        typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
        typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

        languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
        languageRegistry.register(RawLanguageDriver.class);
    }
}

在 Configuration 的構造方法中,進行注冊別名操作時使用了換行符進行分割,它將 TransactionFactory 相關的緊挨在一起作為一組,再將 DataSourceFactory 相關的緊挨在一起等等,這樣在分門別類查看這段代碼便是清晰的,即使它們都在一個方法中。

方法的編排

在《代碼整潔之道》中提出了代碼中 方法要從上到下排列,讀方法就像讀報紙一樣,因為方法被抽象提煉出來,閱讀時必然會造成在多個方法間切換的問題,那么如果我們將方法從上到下依次排列,能夠在屏幕中同時看到所有相關方法的話,那么這樣的確方便了閱讀,比如 methodA 依賴 commonMethod 方法的排列:

@Override
public void methodA() {
    commonMethod();   
}

private void commonMethod() {
    // ...
}

此時如果增加 methodB() 也要復用 commonMethod() 的話,那么我并不會像下面這樣排列方法:

@Override
public void methodA() {
    commonMethod();
}

private void commonMethod() {
    // ...
}

@Override
public void methodB() {
    commonMethod();
}

因為我們在看一個方法時,始終要堅持 自上往下讀 的原則,不能在看 methodB() 的時候,再跳回到上面去,而是需要像這樣:

@Override
public void methodA() {
    commonMethod();
}

@Override
public void methodB() {
    commonMethod();
}

private void commonMethod() {
    // ...
}

那么這也就意味著:如果 某個方法被復用的次數過多,它的位置則越靠近類的下方。在《軟件設計哲學》中也提到過 專用方法上移,通用方法下移 的觀點,這也是在提醒開發者,當看見某個私有方法在類的尾部時,它可能是一個非常通用的方法,對它的修改就需要特別謹慎。

方法的聲明

在業務代碼中經常會看到接口中某方法聲明拋出異常:

public interface Demo {
    void method(Object parameter) throws Exception;
}

但是對要拋出的異常類型并沒有明確的聲明,只知道會拋出 Exception,對于具體的原因一無所知。如果想清楚的了解,可以借助注釋(如果有的話),否則就需要去探究它的具體實現,這對想直接調用該方法的研發人員來說非常不友好,增加了 “認知負荷” ,那該怎么辦呢?

《圖解Java多線程設計模式》中提到過一個例子非常有啟發性,它說方法簽名中標記 throws InterruptedException 能表示兩種含義:第一種比較容易被想到,表示該方法可以被打斷/取消;第二種含義是,這個方法耗時可能比較長

比如 Thread.join() 方法,它聲明了 throws InterruptedException,它的作用是讓當前執行的線程暫停運行,直到調用 join() 方法的線程執行完畢。當我們在一個線程實例上調用 join() 方法時,當前執行的線程將被阻塞,阻塞時間可能會很長,如果在阻塞期間如果另一個線程中斷(interrupt)了它,那么它將拋出一個 InterruptedException。所以,我們能夠在 throws 聲明中,獲取某方法關于某異常的信息

在 Mybatis 源碼中也有類似的例子,如下:

public interface Executor {
    int update(MappedStatement ms, Object parameter) throws SQLException;
}

它聲明出 throws SQLException 表示 SQL 執行的異常,它被拋出了我們便能知道是 SQL 寫的有問題。我認為直接將方法上聲明 throws Exception 的簽名并不添加任何注釋是一種懶惰。異常精細化能給我們帶來很多好處,比如日常報警容易看,增加方法可讀性,能夠通過聲明知道這個方法會拋出關于什么類型的異常,便能讓接口的調用者判斷是處理異常還是拋出異常。

方法的參數聲明也很重要,我認為在業務代碼中除了要遵循方法入參不要過多以外,還需要遵循 隨著重要程度向后排序 的原則,以 Mybatis 中如下方法為反例:

public class DefaultResultSetHandler implements ResultSetHandler {
    // ...
    private final Map ancestorObjects = new HashMap();
    
    private void putAncestor(Object resultObject, String resultMapId) {
        ancestorObjects.put(resultMapId, resultObject);
    }
}

向緩存中添加元素的方法 putAncestor 將入參 String resultMapId 放在第一位更合適。

關于代碼自解釋

每次提到命名或者在為接口命名時,之前我都會有一種非常強烈的讓它自解釋的想法,但是隨著對軟件開發理解的變化,這種想法的欲望在逐漸降低,原因有二:

閱讀習慣:對國人來說,可能大多數人沒有先去讀英文的習慣,更傾向于讀中文相關的內容,比如注釋

英語水平參差:可能有時候想要自解釋的初心是好的,但是如果使接口名變成了長難句,可讀性將降低

當然,花時間來好好為變量和方法命名,是非常值得的,它能大大的提高可讀性,最好的情況是:當讀者看到它時,就已經基本領會了它的作用。盡可能的讓它們明確、直觀且不太長。如果很難為變量或方法找到一個簡單的名稱,這可能暗示底層對象的設計不夠簡潔,《軟件設計哲學》提出了一種觀點:考慮 拆分成多個分別定義 或者為其 添加上必要的注釋。此外,我覺得命名保持一致性也非常重要,比如在項目中對于補購已經命名為 AddBuy,那么便不要再引入 SupplementaryPurchase 和 Replenishment 等命名,團隊內成員將知識統一才是最好的,并不在于它在英文語境下是否表達準確。

但是,Mybatis 為什么能夠在很少注釋的情況下又保證了它的源碼自解釋呢?而且在《代碼整潔之道》中也持有對注釋的消極觀點:

... 注釋最多只能算是一種不得已而為之的手段。若編程語言有足夠的表達力,或者我們長于用這些語言來表達意圖,就不那么需要注釋——也許根本不需要。 注釋的恰當用法是彌補我們在代碼中未能表達清楚的內容... 注釋總是代表著失敗,我們總有不用注釋便很難表達代碼意圖的時候,所以總要有注釋,這并不值得慶賀。

因為 Mybatis 中方法做的事情足夠簡單,像簡單的 query 和 doQuery 方法,或者再復雜一些的 handleRowValuesForNestedResultMap 也能知道它是在處理循環引用的結果映射集。而在業務代碼中就不太一樣了,僅靠幾個簡短的詞語并不能將方法的作用解釋清楚,想讓它自解釋就會導致方法名寫的很長,而且多數情況下,研發同事并不愿意花精力去翻譯那冗長又蹩腳的方法名,給人更多的感受是:“這寫的都是什么?”。如果想在業務代碼中保證“代碼自解釋”的話,還是需要認真的去寫注釋。因為業務功能相對復雜,而方法名本身所能表現的東西又非常有限,通常并不能僅通過方法名來表達其含義,注釋能夠在此處為方法表達帶來增益。但因此認為注釋是彌補方法名表達能力欠佳的補丁,就有些偏頗了,因為隨著注釋寫的越來越多,你會發現:注釋其實是代碼的一部分,它不光提供代碼之外的重要信息,還能隱藏復雜性,提高抽象程度,這還反映了開發者對代碼的設計和重視,隨著時間的推移,有新的開發者加入時,也能讓他快速理解代碼,降低出現 Bug 的概率。

不過,也有一些命名方法能夠幫我們提高方法的可讀性,比如 instantiateXxx 表示創建某對象,initialXxx 表示為某對象中字段賦值。

還有一點值得學習,Mybatis 源碼中會在目錄下創建 package-info.java 來注釋包路徑,以 src/main/java/org/apache/ibatis/cache/decorators/package-info.java 為例,它注釋了該目錄都是緩存的裝飾器:

/**
 * Contains cache decorators.
 */
package org.apache.ibatis.cache.decorators;

這樣我們就能夠知道該路徑下的定義是與什么有關了。不過,這會使得該文件夾雜在各個類之中,如果能在命名前加上 a- 成為 a-package-info.java 被置于頂部的話,會更整潔一些:

“能用就行” 其實遠遠不夠

“代碼整潔與否不是一件主觀的事情,這需要始終站在閱讀者的角度考慮”是學習軟件設計帶給我最大的啟發,“該如何設計能讓開發者更輕松得讀懂”也成了在寫代碼時常常考慮的問題。《軟件設計哲學》中提到過“永遠不要反駁他人對代碼可讀性的評價”的觀點也正是在強調這些。

到現在回看本專欄,發現真正的講好設計原則和代碼的寫法并不是一件很容易的事情,因為我不想只講理論,而想結合實踐又需要結合大部分 Mybatis 源碼,所以它們在內容上,源碼介紹會占得更多一些,當然這也是我覺得稍有遺憾的點,如果這都能給大家帶來一些啟發的話,實在感激涕零。

雖然本專欄始終圍繞著如何將代碼寫得更整潔和優雅做討論,但是我們還是需要學會“負重前行”:和凌亂的代碼相處。一些凌亂的代碼可能寫過一次后便不再變更,所以有時候沒有必要為了優雅強迫癥而去重構它們,它們可能始終會被隱藏在某個方法后面,默默地提供著穩定的功能,如果你深受其擾,可以考慮在你讀過之后為這段代碼添加注釋,之后看這段代碼的開發者也能理解和感謝你的用心,否則因為優雅的重構導致線上生產事故,可就得不償失了。

實際上,能寫好代碼對于程序員來說并不是一件特別厲害的事情,它只能算是一項基本要求,而且隨著 AI 的不斷發展,它在未來可能會幫我們生成很好的設計。當然,這也不是放任的理由,寫爛代碼的行為還是需要被摒棄的。在最后我想借先前讀過的雷軍的博客《我十年的程序員生涯》的節選來結束本專欄:

有的人學習編程技術,是把高級程序員做為追求的目標,甚至是終身的奮斗目標。后來參與了真正的商品化軟件開發后,反而困惑了,茫然了。

一個人只要有韌性和靈性,有機會接觸并學習電腦的編程技術,就會成為一個不錯的程序員。剛開始寫程序,這時候學得多的人寫的好,到了后來,大家都上了一個層次,誰寫的好只取決于這個人是否細心、有韌性、有靈性。掌握多一點或少一點,很快就能補上。成為一個高級程序員并不是件困難的事。

當我上學的時候,高級程序員也曾是我的目標,我希望我的技術能得到別人的承認。后來發現無論多么高級的程序員都沒用,關鍵是你是否能夠出想法出產品,你的勞動是否能被社會承認,能為社會創造財富。成為高級程序員絕對不是追求的目標

希望大家不僅能寫出好代碼,還能做出屬于自己的產品,為生活乃至世界添一份彩。

審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 源碼
    +關注

    關注

    8

    文章

    651

    瀏覽量

    29339
  • mybatis
    +關注

    關注

    0

    文章

    61

    瀏覽量

    6730
收藏 人收藏

    評論

    相關推薦

    一文了解MyBatis的查詢原理

    可以詳細了解MyBatis的一次查詢過程。在平時的代碼編寫中,發現了MyBatis一個低版本的bug(3.4.5之前的版本),由于現在很多工程中的版本都是低于3.4.5的,因此在這里用一個簡單的例子復現問題,并且從源碼角度分析
    的頭像 發表于 10-10 11:42 ?1450次閱讀

    史上最專業的電容選型資料

    1. MLCC選型:僅僅滿足參數還遠遠不夠作者:桂軍 電子元件技術網產品需求分析師購買商品的一般決策邏輯是:能不能用,好不好用,耐不耐用,價格。其實這個邏輯也可以套用到MLCC的選型過程中:首先
    發表于 06-27 16:15

    求一款可以與51單片機通訊的LCD顯示屏,12864遠遠不夠

    各位大神求一款可以與51單片機通訊的LCD顯示屏,12864遠遠不夠大,最好有5寸以上的,還要與單片機能夠通訊。
    發表于 06-24 15:42

    Mybatis的內部設計介紹

    Mybatis源碼分析-整體設計(一)
    發表于 06-06 09:43

    數據庫整合Mybatis框架

    微服務 SpringBoot 20():整合Mybatis
    發表于 07-16 11:03

    傳感器是什么什么組成

    傳感器的概念來自“感覺(sensor)”一詞,人們為了研究自然現象,僅僅依靠人的五官獲取外界信息是遠遠不夠的,于是發明了能代替或補充人五官功能的傳感器,工程上也將傳感器稱為“變換器”。 
    發表于 04-23 06:41

    軟件設計師全書

    軟件設計師全書有軟件設計師教程,軟件設計師考試輔導,考點精講,例題分析,強化訓練,軟件設計課程課件等內容。
    發表于 10-29 17:45 ?0次下載
    <b class='flag-5'>軟件設計</b>師全書

    中國彩電市場銷量遠遠不夠?我們的市場還遠遠沒有到消費的天花板

    中國電子視像行業協會常務副會長白為民認為,我們的市場還遠遠沒有到消費的天花板,與英國、俄羅斯等國家相比,中國的彩電銷量和人口數量還不匹配,中國彩電市場還有很大的發展空間。按照我們的人口比例,一年5000多萬臺的銷量是遠遠不夠的。
    發表于 07-22 11:54 ?1034次閱讀

    人工智能技術感知層面 認知智能發展遠遠不夠

    人工智能技術集中感知層面 認知智能發展遠遠不夠,認知智能,涉及語義理解、知識表達、聯想推理、智能問答、自主學習等,目前已有較為廣泛的應用。認知智能的發展將使大量繁瑣卻重要的工作變得更加高效精準,也更
    發表于 07-04 09:53 ?1850次閱讀
    人工智能技術感知層面 認知智能發展<b class='flag-5'>遠遠不夠</b>

    基于單片機的避障小車及自動循跡的設計(proteus仿真+源碼+原理圖+軟件設計流程+硬件清單+視頻講解)

    基于單片機的避障小車及自動循跡的設計(proteus仿真+源碼+原理圖+軟件設計流程+硬件清單+視頻講解)
    發表于 11-11 15:51 ?92次下載
    基于單片機的避障小車及自動循跡的設計(proteus仿真+<b class='flag-5'>源碼</b>+原理圖+<b class='flag-5'>軟件設計</b>流程+硬件清單+視頻講解)

    【畢設狗】【單片機畢業設計】基于單片機的教室人數實時檢測系統的設計(proteus仿真+源碼+原理圖+軟件設計

    【畢設狗】【單片機畢業設計】基于單片機的教室人數實時檢測系統的設計(proteus仿真+源碼+原理圖+軟件設計流程+硬件清單+視頻講解)
    發表于 11-12 19:36 ?12次下載
    【畢設狗】【單片機畢業設計】基于單片機的教室人數實時檢測系統的設計(proteus仿真+<b class='flag-5'>源碼</b>+原理圖+<b class='flag-5'>軟件設計</b>

    關于基于單片機的8層電梯控制系統的設計(proteus仿真+源碼+原理圖+軟件設計流程+硬件清單+視頻講解)

    關于基于單片機的8層電梯控制系統的設計(proteus仿真+源碼+原理圖+軟件設計流程+硬件清單+視頻講解)
    發表于 11-12 20:06 ?25次下載
    關于基于單片機的8層電梯控制系統的設計(proteus仿真+<b class='flag-5'>源碼</b>+原理圖+<b class='flag-5'>軟件設計</b>流程+硬件清單+視頻講解)

    【畢設狗】【單片機畢業設計】基于單片機的家用應急電源的設計(proteus仿真+源碼+原理圖+軟件設計流程+硬件

    【畢設狗】【單片機畢業設計】基于單片機的家用應急電源的設計(proteus仿真+源碼+原理圖+軟件設計流程+硬件清單+視頻講解)
    發表于 11-12 20:21 ?10次下載
    【畢設狗】【單片機畢業設計】基于單片機的家用應急電源的設計(proteus仿真+<b class='flag-5'>源碼</b>+原理圖+<b class='flag-5'>軟件設計</b>流程+硬件

    【畢設狗】【單片機畢業設計】基于單片機的智能密碼鎖的設計(實物+proteus仿真+源碼+原理圖+軟件設計流程

    【畢設狗】【單片機畢業設計】基于單片機的智能密碼鎖的設計(實物+proteus仿真+源碼+原理圖+軟件設計流程+硬件清單+視頻講解)
    發表于 11-13 09:06 ?34次下載
    【畢設狗】【單片機畢業設計】基于單片機的智能密碼鎖的設計(實物+proteus仿真+<b class='flag-5'>源碼</b>+原理圖+<b class='flag-5'>軟件設計</b>流程

    源碼學習之MyBatis的底層查詢原理

    可以詳細了解MyBatis的一次查詢過程。在平時的代碼編寫中,發現了MyBatis一個低版本的bug(3.4.5之前的版本),由于現在很多工程中的版本都是低于3.4.5的,因此在這里用一個簡單的例子復現問題,并且從源碼角度分析
    的頭像 發表于 10-10 11:42 ?833次閱讀
    主站蜘蛛池模板: a三级黄色片| 亚洲精品拍拍央视网出文| 久久国产欧美| 久久精品18| 亚洲高清免费在线观看| 2019夜夜| 99热在线观看| 免费一区二区三区久久| 入禽太深免费视频10| 雪恋电影完整版免费观看| 970女主播电台歌曲| 东日韩二三区| 极品 女神校花 露脸91| 免费在线视频a| 97人人看碰人免费公开视频 | 在线亚洲视频无码天堂| china中国gay偷拍| 年轻夫妇韩剧中文版免费观看 | 亚洲一区免费香蕉在线| WWW久久只有这里有精品| 嘿嘿视频在线观看 成人| 男男高h浪荡受h| chaopeng 在线视频| 日本一二三区在线视频| 中文字幕不卡免费高清视频| 国产精品资源在线观看网站| 奇米狠狠干| 文中字幕一区二区三区视频播放| 13小箩利洗澡无码视频APP| 麻豆精品乱码WWW久久密| 午夜福利92看看电影80| caoporn免费视频在线| 日本红怡院亚洲红怡院最新| 国产高清免费视频免费观看| 欧美MV日韩MV国产网站| 亚洲日韩在线观看| 国产自拍视频在线一区| 午夜免费啪视频观看视频| 狠狠啪在线香蕉| 52色擼99热99| 美女张开腿露出尿口扒开来摸动漫|