API設(shè)計(jì)的一些核心原則

沒(méi)有經(jīng)過(guò)設(shè)計(jì)的代碼,是沒(méi)有靈魂的。


魯迅沒(méi)說(shuō)過(guò)這句話

設(shè)計(jì)涵蓋的內(nèi)容很廣,而API設(shè)計(jì)是與日常開(kāi)發(fā)關(guān)系比較緊密的一塊。換句話說(shuō),掌握了一些核心的設(shè)計(jì)要領(lǐng),對(duì)代碼質(zhì)量提升是立竿見(jiàn)影的。

無(wú)規(guī)矩不成方圓,API設(shè)計(jì)一樣。最近學(xué)習(xí)了Google工程師總結(jié)的API設(shè)計(jì)原則,看完后,腦海中回憶起自己寫(xiě)的代碼,方法的命名、參數(shù)順序、異常處置、功能范圍,無(wú)一不被作者所戳中。我覺(jué)得很有價(jià)值,所以拿來(lái)與大家一起分享,相信對(duì)你肯定有幫助。當(dāng)然,作者總結(jié)的比較簡(jiǎn)潔,具體含義,需要大家思考并深入的體會(huì),相信經(jīng)過(guò)一番思考以后,定有收獲。

由于原文是生肉,為了給部分小伙伴提供一些便利,我進(jìn)行了翻譯,翻譯不好的地方還請(qǐng)指正,在此表示感謝。當(dāng)然有喜歡生肉的同學(xué),我也會(huì)在文末評(píng)論處貼上原文地址,請(qǐng)自取。

以下是正文部分。


image

API設(shè)計(jì)原則

通用原則

api應(yīng)該只做一件事,并且要做好

  • 功能應(yīng)易于解釋
  • 如果很難對(duì)一個(gè)功能命名,通常是一個(gè)壞兆頭
  • 好名稱驅(qū)動(dòng)開(kāi)發(fā)
  • 模塊能夠拆分和合并

最小化內(nèi)容訪問(wèn)權(quán)限

  • 類(lèi)和成員變量盡可能私有化
  • 公共類(lèi)不應(yīng)該有公共變量(常量除外)
  • 最大化信息隱藏
  • 允許模塊能夠獨(dú)立的使用、理解、構(gòu)建、測(cè)試、調(diào)試

名字很重要 -api是一種小語(yǔ)言

  • 命名在很大程度上應(yīng)該是不言自明的(避免過(guò)于隱晦的縮寫(xiě))
  • 保持一致,同一個(gè)詞應(yīng)該始終代表一個(gè)含義(在整個(gè)api、跨平臺(tái)的api)
  • 保持規(guī)律性-力求對(duì)稱
  • 代碼應(yīng)該讀起來(lái)像散文,如:
if (car.speed() > 2 * SPEED_LIMIT){
    generateAlert("Watch out for cops!);
}

文檔很重要

重用是一種說(shuō)起來(lái)容易做起來(lái)難的事情。要做到這一點(diǎn),需要良好的設(shè)計(jì)和非常好的文檔。即使當(dāng)我們看到良好的設(shè)計(jì)(這仍然是不常見(jiàn)的)時(shí),如果沒(méi)有良好的文檔,我們也不會(huì)看到組件被重用。
- D. L. Parnas, _Software Aging.
Proceedings
of 16th International Conference Software
Engineering, 1994

虔誠(chéng)的寫(xiě)文檔

  • 為類(lèi)、接口、方法、參數(shù)、異常、構(gòu)造器編寫(xiě)文檔
    • 類(lèi):實(shí)例代表什么
    • 方法:方法與使用者之間的約定,如先決條件、后決條件、副作用
    • 參數(shù):表示單位、表單、所有權(quán)

api設(shè)計(jì)決策要考慮性能影響

  • 不好的決策可能會(huì)限制性能,如:
    • 類(lèi)型可變(參考下一條的例子)
    • 使用構(gòu)造方法替換工廠
    • 使用實(shí)現(xiàn)類(lèi)型代替接口
  • 不要試圖通過(guò)包裝api來(lái)提升性能
    • 好的設(shè)計(jì)通常伴隨好的性能

api設(shè)計(jì)決策對(duì)性能的影響是真實(shí)并且持久的

  • Component.getSize() 返回 Dimension
  • Dimension是可變的
  • 每次調(diào)用getSize()將會(huì)分配一個(gè)Dimension
  • 導(dǎo)致數(shù)百萬(wàn)不必要的對(duì)象
  • 即使在1.2版本中增加新方案,老的代碼依舊執(zhí)行很慢

api必須與平臺(tái)和平共存

  • 按規(guī)律辦事
    • 遵守標(biāo)準(zhǔn)的命名約定
    • 避免廢棄的參數(shù)類(lèi)型和返回類(lèi)型
  • 利用好api的特性
    • 泛型、枚舉、默認(rèn)參數(shù)、varargs
  • 了解并避免api的陷阱
    • Finalizers, public static final arrays

類(lèi)設(shè)計(jì)

減少可變性

  • 類(lèi)應(yīng)該是不可變的,除非有更好的理由
    • 優(yōu)點(diǎn):簡(jiǎn)單、線程安全、可重用
    • 缺點(diǎn):每個(gè)值都有單獨(dú)的對(duì)象
  • 如果是可變的,保持狀態(tài)空間盡可能小和良好的定義
    • 明確何時(shí)調(diào)用那個(gè)方法是合法的

Bad: Date, Calendar
Good: TimerTask

子類(lèi)只出現(xiàn)在有意義的地方

  • 子類(lèi)化意味著可替代性
    • 僅當(dāng)is_a關(guān)系才出現(xiàn)子類(lèi)
    • 否則,請(qǐng)使用組合
  • 公共類(lèi)不應(yīng)該僅僅為了容易實(shí)現(xiàn)而子類(lèi)化其他公共類(lèi)

Bad: Properties extends Hashtable, Stack extends Vector
Good: Set extends Collection

設(shè)計(jì)和文檔用于繼承,否則就禁止它

  • 繼承違反了封裝原則,子類(lèi)對(duì)超類(lèi)的實(shí)現(xiàn)細(xì)節(jié)敏感
  • 如果你允許子類(lèi)化,文檔留作自用
  • 保守策略:所有的具體類(lèi)都final化

Bad: Many concrete classes in J2SE libraries
Good: AbstractSet, AbstractMap

方法設(shè)計(jì)

不要讓使用者做任何模塊可以做的事情

  • 減少對(duì)樣板代碼的需求
    • 通常通過(guò)剪切和粘貼來(lái)完成
    • 丑陋,煩人,容易出錯(cuò)
image.png
image.png

不要違反最小化原則

  • api的用戶不應(yīng)該對(duì)api的行為感到驚訝
  • 這值得付出很大的努力,甚至犧牲一些性能
public class Thread implements Runnable {
    // Tests whether current thread has been interrupted.
    // Clears the interrupted status of current thread.
    public static boolean interrupted();
    }
}

快速失敗-當(dāng)發(fā)生錯(cuò)誤時(shí),盡可能早的報(bào)錯(cuò)

  • 編譯時(shí)最好- 靜態(tài)類(lèi)型、泛型
  • 運(yùn)行時(shí),首先調(diào)用發(fā)生錯(cuò)誤的方法
    • 方法應(yīng)該是原子失敗的

使用適當(dāng)?shù)膮?shù)和返回類(lèi)型

  • 在輸入時(shí),優(yōu)先選擇接口類(lèi)型而不是類(lèi)
    • _提供靈活性、性能
  • 使用最具體的輸入?yún)?shù)類(lèi)型
    • 將錯(cuò)誤從運(yùn)行時(shí)移動(dòng)到編譯時(shí)
  • 如果存在更好的類(lèi)型,不要使用string
    • 字符串是笨重的,容易出錯(cuò)的,而且很慢
  • 不要使用浮點(diǎn)數(shù)來(lái)表示貨幣值
    • 二進(jìn)制浮點(diǎn)導(dǎo)致不精確的結(jié)果!
  • 使用double(64位)而不是float(32位)
    • 精度損失是真實(shí)的,性能損失可以忽略不計(jì)

使用一致的參數(shù)排序

  • 尤其重要的是,如果參數(shù)類(lèi)型相同
  • 如Collections中的方法,第一個(gè)參數(shù)一般都是被修改或被查詢的。
image.png
image.png

image.png
image.png

避免長(zhǎng)的參數(shù)列表

  • 三個(gè)或更少的參數(shù)是理想的
    • 更多的用戶將不得不參考文檔
  • 一長(zhǎng)串類(lèi)型相同的參數(shù)是有害的
    • _程序員誤轉(zhuǎn)了參數(shù)
    • 程序仍在編譯、運(yùn)行,但行為不正常!
  • 兩種縮短參數(shù)列表的技術(shù)
    • 拆分方法
    • 創(chuàng)建助手類(lèi)來(lái)保存參數(shù)

避免需要異常處理的返回值

  • 返回零長(zhǎng)度數(shù)組或空集合,而不是null

Bad:


image.png
image.png

異常設(shè)計(jì)

支持unchecked異常

?checked-客戶必須采取恢復(fù)措施
?unchecked–編程錯(cuò)誤
?過(guò)度使用checked的異常會(huì)導(dǎo)致樣板


image.png
image.png

在異常中包含失敗捕獲信息

  • 允許診斷和修復(fù)或恢復(fù)
  • 對(duì)于未檢查的異常,消息就足夠了
  • 對(duì)于已檢查的異常,提供訪問(wèn)器
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容