浅谈API设计
為什么需要了解一些API設(shè)計(jì)?
只要你編程,你就是API Designer
一個(gè)好的設(shè)計(jì),模塊之間的耦合應(yīng)該也是API級(jí)別的
一個(gè)程序,如果你獨(dú)立開發(fā),那你既是API的Designer,也是API的User
如果你和你的同事一起開發(fā),,你既是你開發(fā)的模塊API的Designer,也是其他同事模塊API的User
一個(gè)好的API應(yīng)該具備哪些特點(diǎn)?
1. 易學(xué)易用(Easy to learn and use)
要做到易學(xué)易用,需要滿足以下基本要求:
a. API命名的要適應(yīng)用戶的習(xí)慣并遵循一些模式。例如:使用類似于add/delete, get/set,push_bach/pop_back這種大家比較熟悉的命名方法。避免用戶使用get獲取一個(gè)數(shù)據(jù),但是需要使用insert插入數(shù)據(jù)。
b. API的設(shè)計(jì)需要盡量簡(jiǎn)化用戶的使用復(fù)雜度,我們看下STL vector的使用,非常簡(jiǎn)潔!
c.盡量提供一些方便用戶的api方法
例如提供一個(gè)STL vector提供at方法供用戶獲取指定位置的元素
但同時(shí)也提供API供用戶直接獲取第一個(gè)和最后一個(gè)元素
reference front(); reference back();2. 引導(dǎo)用戶寫出可讀性高的代碼(Leads to readable code)
好的API設(shè)計(jì),可以引導(dǎo)用戶寫出高可讀性的代碼,下面這個(gè)例子非常生動(dòng):
實(shí)現(xiàn) 1:
實(shí)現(xiàn) 2:
slider = new QSlider(Qt::Vertical); slider->setRange(8, 128); slider->setValue(6); slider->setObjectName("volume");顯然第二種設(shè)計(jì)更易引導(dǎo)用戶寫出可讀性高的代碼
3. 很難被誤用
一個(gè)好的API設(shè)計(jì),會(huì)使用戶很容易寫出正確的代碼,而不是錯(cuò)誤的代碼。
這點(diǎn)非常重要,如果你的API很容易用錯(cuò),那么,一方面用戶會(huì)吐槽,另一方面,API的維護(hù)成本也很高
so,當(dāng)你收到很多錯(cuò)誤報(bào)告的時(shí)候,不要抱怨用戶的使用方法有問題,review下自己的API,是不是很容易被誤用
4. 方便擴(kuò)展(Easy to extend)
api會(huì)變的越來越“大”,未來,api會(huì)提供新的類,類中會(huì)有新的方法,方法會(huì)有新的參數(shù),枚舉數(shù)據(jù)也會(huì)有新的枚舉值。
因此,在API設(shè)計(jì)過程中,需要時(shí)刻提醒自己保證API的可擴(kuò)展性
5. 完整(Complete)
所謂的完整,并不是指API滿足用戶所有的功能需求,而是說,基于這些API,可以滿足所有功能需求。
我們還是拿STL::Vector來做說明,從獲取數(shù)據(jù)的角度,API提供了reference at (size_type n); 就是完整的,另外提供的front back都是方便用戶獲取數(shù)據(jù),用戶完全可以個(gè)性化實(shí)現(xiàn)這樣的功能。但是,如果只提供了front、back,而沒有提供at這樣的api,就是不完整的
兼容性
在設(shè)計(jì)API時(shí),非常重要的點(diǎn)是要提前考慮API的發(fā)布方式,在發(fā)布過程中,兼容性非常重要
我們可以將兼容性分為:源代碼兼容,二進(jìn)制兼容,功能兼容
在Deliver新版本的API時(shí),需要明確告知用戶,API版本在哪些層面做到了向后兼容
源代碼向后兼容:老的程序,使用新的API庫依然能夠正常工作
二進(jìn)制向后兼容:直接升級(jí)庫文件,老的程序不必重新編譯就能使用新的庫文件
功能兼容向后兼容:老的程序,使用新的API庫,不影響已有功能
功能兼容是業(yè)務(wù)的范疇,這里不展開。著重討論下源代碼兼容和二兼職兼容。一個(gè)很有意思的討論Source compatible vs. Binary compatible
一般來說,二進(jìn)制兼容是源代碼兼容的子集。如果你的代碼打破了源代碼兼容,基本上可以肯定你也打破了二進(jìn)制兼容。相反則不成立,一個(gè)API是源代碼兼容但不是二進(jìn)制兼容表示你的代碼無需如何修改,只要重新編譯程序即能正常工作。
我們看下c++來,什么情況下我們會(huì)滿足源代碼兼容,但不滿足二進(jìn)制兼容
c++發(fā)布api有靜態(tài)和動(dòng)態(tài)庫兩種方式,這兩種發(fā)布方式各有優(yōu)劣,當(dāng)采取動(dòng)態(tài)庫的發(fā)布方式,就面臨二進(jìn)制兼容問題
c++通過頭文件使用動(dòng)態(tài)庫,編譯時(shí)也據(jù)此產(chǎn)生二進(jìn)制代碼,因此,在考慮動(dòng)態(tài)庫兼容性的時(shí)候,只要考慮庫使用已有的頭文件是否和新的動(dòng)態(tài)庫兼容
c++使用動(dòng)態(tài)庫最常見的二進(jìn)制兼容問題就是使用虛函數(shù)作為接口產(chǎn)生的。c++虛函數(shù)通過虛函數(shù)表實(shí)現(xiàn),虛函數(shù)表具有以下特點(diǎn):
1)虛函數(shù)按照聲明順序放在表中
2)父類的虛函數(shù)放在子類的虛函數(shù)之前
so,如果我們?cè)谛掳姹镜膭?dòng)態(tài)庫中插入新的接口定義,由于虛函數(shù)的順序,導(dǎo)致二進(jìn)制兼容問題
即使你在你的接口中將新增虛函數(shù)放在最后,由于接口可能被繼承,而父類的虛函數(shù)在子類虛函數(shù)之前,因此還是會(huì)導(dǎo)致二進(jìn)制兼容性問題
為了解決這個(gè)問題,我們可以采用pimpl來解決!
有一篇非常棒的文章,C++ 工程實(shí)踐(5):避免使用虛函數(shù)作為庫的接口
一些API開發(fā)的建議:
1. 踐行TDD
2. 如果可能,盡早和API使用方溝通
3. 重視命名,它比你想象的更重要
Reference:
Joshua Bloch:How to Design a Good API and Why it matters
Jasmin Blanchette Trolltech :The Little Manual of API Design
Kinds of Compatibility: Source, Binary, and Behavioral
?
總結(jié)
- 上一篇: debian 下修改boot停留时间
- 下一篇: java中如何将非整数保留到小数点后指定