要滿足的需求
滿足功能需求
首先,要設(shè)計(jì)一個(gè)SDK一定要考慮用戶需求,確定邊界,SDK需要包含哪些功能。個(gè)人認(rèn)為SDK應(yīng)當(dāng)精簡,專注實(shí)現(xiàn)一部分功能即可。
滿足性能要求
此外除了功能性需求,軟件設(shè)計(jì)開發(fā)中還需要考慮非功能性需求。比方說,穩(wěn)定性、性能、安全等。
- 穩(wěn)定性:如果sdk不穩(wěn)定,功能時(shí)好時(shí)壞,用戶是不是很懵逼。如果sdk有bug,導(dǎo)致了宿主應(yīng)用崩潰,對實(shí)際用戶造成體驗(yàn)多不好。故而一個(gè)高水準(zhǔn)的sdk一定是一個(gè)穩(wěn)定性非常好的sdk。
- 性能:性能實(shí)際包含多個(gè)方面,比方說包體積、電量、內(nèi)存等。這些因素對于SDK設(shè)計(jì)者挑戰(zhàn)非常大,但又是對用戶影響十分深遠(yuǎn)的部分。要設(shè)計(jì)好一個(gè)sdk,性能必須滿足一定標(biāo)準(zhǔn)。
- 安全:前兩者是面向用戶的,安全大多數(shù)情況是對自己而言的。在編寫sdk中,應(yīng)當(dāng)要考慮如何保證容器代碼安全,保證核心數(shù)據(jù)、接口不被暴露,保證核心代碼不被獲取。
遵循的幾個(gè)基本原則
接口隔離
設(shè)計(jì)應(yīng)當(dāng)小而精簡;
開閉原則。
對于擴(kuò)展開放,對于內(nèi)部修改封閉。對外交互部分盡量面向接口編程,實(shí)現(xiàn)抽象。
接口的易用性
設(shè)計(jì)符合“人性”的接口,面向“大眾”的接口,減少奇技淫巧的使用。sdk是給別人使用的,不是用來炫技的,樸實(shí)就好。
向后兼容
不能讓用戶升級你的sdk后原有功能不可用了。
編寫文檔
最后,完成sdk編碼工作后,一定要記得編寫文檔。俗話說,程序員最討厭兩件事,一個(gè)是寫文檔,另一個(gè)是使用sdk沒有文檔。一份好的文檔能讓用戶事半功倍,快速接入使用sdk,并減少后續(xù)bug。
接口設(shè)計(jì)的原則
設(shè)計(jì)原則
1、接口名稱、參數(shù)名稱要足夠清晰
一個(gè)牛逼的接口名稱,可以替代無數(shù)的注釋
2、一個(gè)接口只做一件事
- 一個(gè)接口只做一件事。如果有兩個(gè)比較接近的功能,但是用一個(gè)接口實(shí)現(xiàn)有點(diǎn)麻煩,那就用兩個(gè)接口,不要為了減少接口而生硬的把兩個(gè)接口合為一個(gè)。
3、接口參數(shù)要盡可能少
- 接口調(diào)用的參數(shù)要盡可能少,SDK能自身獲取的就不要讓開發(fā)者繼續(xù)傳遞,盡可能少的在一個(gè)接口中使用同一數(shù)據(jù)類型的參數(shù),如果確實(shí)很多,建議封裝
Object作為參數(shù)。
接口參數(shù)要一定要校驗(yàn)、需要轉(zhuǎn)義或者轉(zhuǎn)換的一定要盡可能早的處理:
- 所有接口參數(shù)必須要做合法性校驗(yàn)。不要讓別的接口去保證調(diào)用接口的參數(shù)一定是合法的。
- 所有接口做的第一件事就應(yīng)該是對參數(shù)做合法性校驗(yàn)。不要等到邏輯跑完大半了再告訴參數(shù)不合法,調(diào)用失敗。
- 對于需要轉(zhuǎn)義、需要類型轉(zhuǎn)換等的參數(shù),一定要處理,而且盡量盡早的去處理,雖然客戶端沒有XSS或者SQL注入什么的,不代表你就可以不用考慮
4、通用的名稱要統(tǒng)一
-
即使再小的系統(tǒng),也會(huì)有一些通用名詞,對于一些通用名詞或者模塊的叫法、寫法一定要統(tǒng)一。
具體來說就是一些通用名詞的寫法一定要規(guī)范,其實(shí)大多數(shù)可以通過編碼規(guī)范來避免,例如
openid和open_id這種只要編碼規(guī)范統(tǒng)一就不會(huì)同時(shí)出現(xiàn)。但是對于具體的名詞就不好說了,比如openid,openID,openId,OpenID。這個(gè)我還能忍,但是對于QQ的寫法我就不能忍了,QQ已經(jīng)是一個(gè)名詞,是一個(gè)標(biāo)準(zhǔn)的寫法了,在QQ結(jié)構(gòu)中看到qq也就算了,竟然還見過有人寫Qq,這是什么鬼,什么鬼???!再舉一個(gè)微信的例子,寫WX,wechat,weChat,wx,weixin的都有,這么多寫法,暈不暈?讓新來的同學(xué)怎么搞~~
5、關(guān)于同步和異步接口
- 可以同步的接口,一定不要異步
- 能不用全局回調(diào)就一定不要用全局的回調(diào)
- 一定要用全局回調(diào)最好按照模塊分開,一個(gè)模塊一個(gè)回調(diào)。開發(fā)者只需實(shí)現(xiàn)他關(guān)心模塊的回調(diào)即可。無關(guān)模塊的回調(diào)設(shè)置是否對SDK的正常使用沒有影響;另外每一個(gè)回調(diào)里最好又能區(qū)分回調(diào)屬于哪次調(diào)用的字段。
- 同一個(gè)回調(diào)里面的接口盡可能的少,可以合并的盡量合并。
個(gè)人感覺總結(jié)的已經(jīng)比較到位了,就再講講我們怎么做的以及有什么優(yōu)缺點(diǎn)。最開始我們所有模塊公用同一個(gè)全局回調(diào),因?yàn)楫?dāng)時(shí)模塊也不多,而且都是必接模塊,最關(guān)鍵是多個(gè)回調(diào)開發(fā)者接入的時(shí)候比較麻煩,所以選擇了不區(qū)分模塊把所有回調(diào)統(tǒng)一整合到一個(gè)全局回調(diào)。
隨著業(yè)務(wù)發(fā)展,我們的SDK包含了越來越多的模塊,有些模塊是屬于個(gè)性化的,小眾需求的,再把他合到通用的全局回調(diào)并不合適,因?yàn)楹芏嚅_發(fā)者并不使用這部分功能,卻還要關(guān)心對應(yīng)的回調(diào)。因此對于這些完全獨(dú)立而且小眾的需求開始使用自己的獨(dú)立的全局回調(diào)。這樣雖然解決了所有的問題,但是感覺太像小作坊的模式。因此建議還是最好一開始就按照模塊各自設(shè)置獨(dú)立的全局回調(diào)。
最新補(bǔ)充,最新的SDK中,我們已經(jīng)在逐步棄用全局回調(diào),直接在接口調(diào)用的時(shí)候讓同步添加對應(yīng)的接口回調(diào)。
6、多線程處理
-
UI線程處理
-
SDK除非必須,不要使用應(yīng)用的主線程,就算使用也只能是簡單操作,不能長時(shí)間占用。 -
SDK應(yīng)該有一個(gè)專門的線程來處理SDK相關(guān)的操作。
-
-
怎么使用handle
-
所有耗時(shí)、異步操作都通過
handle扔給SDK的線程去處理。處理結(jié)束以后再把結(jié)果通過handle發(fā)給主線程。 - 任何時(shí)候主線程制作一件事,UI調(diào)整。所有的耗時(shí)操作:讀取文件、讀取DB、網(wǎng)絡(luò)數(shù)據(jù)讀取、網(wǎng)絡(luò)請求發(fā)起等全部都要不要用UI線程去處理。
-
所有耗時(shí)、異步操作都通過
7、關(guān)于第三方平臺
7.1 關(guān)于因第三方平臺的限制引起的失敗
每個(gè)SDK不可能都是完全獨(dú)立的一部分,尤其是為業(yè)務(wù)服務(wù)的SDK,很可能都還會(huì)和周邊SDK有一些交集。因此對于怎么處理和周邊平臺相關(guān)的一些邏輯也比較麻煩。這里簡單匯總下從客戶端的角度認(rèn)為需要關(guān)注的點(diǎn)。
- 對于非SDK內(nèi)部邏輯的限制引起的接口不可用,不要直接判定為失敗,而是讓規(guī)則制定方去判定。而SDK本身可以加個(gè)Log提示或者打印Log,方便定位問題。
原因有二:
- 平臺的規(guī)則可能會(huì)調(diào)整,到時(shí)候平臺支持了,你不支持,就是你的問題
- 如果按照上面的方法操作,即使平臺調(diào)整了,你也不用專門調(diào)整,尤其如果是客戶端SDK,游戲也不需要更新。
還是說具體的案例吧,不然顯得干巴巴。我們的SDK會(huì)集成多個(gè)SDK,然后對于某些接口平臺會(huì)有一些限制,比如分享的時(shí)候圖片大小不能超過10M,縮略圖不能超過32K等。最開始我們在做參數(shù)校驗(yàn)的時(shí)候發(fā)現(xiàn)圖片大小超過規(guī)范以后打算直接返回失敗,后來經(jīng)過討論,我們決定在超過大小以后打印一行錯(cuò)誤日志,還是會(huì)去繼續(xù)調(diào)用平臺的接口,然后由平臺接口調(diào)用失敗以后回調(diào)給我們,我們再回調(diào)游戲。加上LOG以后游戲聯(lián)調(diào)的時(shí)候,如果調(diào)用失敗了,我們可以根據(jù)LOG快速定位到原因。上線一段時(shí)間以后,很多游戲反映32K太小了,果然和預(yù)期一致平臺將大小改到了64K。那一瞬間,幸福如花兒一般綻放。
7.2 關(guān)于第三方平臺的常量
- 對于第三方平臺的常量例如錯(cuò)誤碼等,最好是自己封裝一層提供給開發(fā)者。不要直接將第三方平臺暴露給開發(fā)者。
很多時(shí)候,開發(fā)者會(huì)通過一個(gè)SDK來同時(shí)集成和實(shí)現(xiàn)多個(gè)SDK的功能,這個(gè)時(shí)候開發(fā)者面對的只有一個(gè)SDK。如果你的SDK包含了或者集成了多個(gè)第三方的SDK,你要做的就是不要讓開發(fā)者還需要了解其余SDK的東西。包括接入配置等。對于第三方平臺的常量,不管是SDK自身還是最終的使用者,其實(shí)不會(huì)再調(diào)整但是SDK怎么處理還是很重要。不封裝,開發(fā)者還需要關(guān)注周邊SDK的內(nèi)容,顯得你不夠?qū)I(yè);封裝的話,萬一第三方SDK有調(diào)整你也要調(diào)整(不要說不可能,我們就親身經(jīng)歷了一個(gè)第三方SDK把常量名稱和值都修改了的例子)。
為了防止上面的情況,建議封裝第三方平臺的常量的時(shí)候不要使用對方的常量值,而是直接使用對方的變量來賦值。例如:
public final static int eXXX = otherPlatform.eXXX;
7.3 第三方平臺的配置
- 對于第三方平臺的各種配置,比如
appid等,最好是僅僅用于第三方平臺的邏輯中,不要為了省事把第三方平臺的配置用于自己的業(yè)務(wù)邏輯
這個(gè)其實(shí)根據(jù)平臺的自身需要了,建議既然是SDK,就是一個(gè)平臺,還是有自己應(yīng)用標(biāo)識比較好(appid,appkey),雖然一開始用不到,但是又沒有壞處。不要依賴第三方平臺的一些應(yīng)用配置信息做自己的邏輯。
關(guān)于配置
這里主要說一些關(guān)于模塊開關(guān),平臺配置相關(guān)的內(nèi)容。主要涉及到配置文件的處理和下發(fā)。
配置通過什么形式下發(fā)
不管是模塊的開關(guān)還是接口的權(quán)限,都應(yīng)該可以后臺控制。當(dāng)然前臺最好也要有配置文件,可以減少一些無用的請求。而且在后臺不能控制的時(shí)候,前臺的開關(guān)還是很有必要的。
在同時(shí)有前后臺的開關(guān)或者配置的時(shí)候,記得優(yōu)先使用后臺的配置,不然你搞個(gè)后臺開關(guān)有毛用
配置怎么存放
配置通??梢砸詐list文件存放在bundle里,也可以放在assert目錄下。對于服務(wù)端下發(fā)的配置文件,可以存放到plist文件里,或者數(shù)據(jù)庫中。
配置使用什么格式
- 所有的配置文件用
key-value的方式來保存 - 所有的配置項(xiàng)的key建議增加統(tǒng)一的淺醉,例如
MSDK_。不加的話,配置項(xiàng)多了會(huì)顯得有點(diǎn)混亂。