API 設計的七宗罪
本文譯自 Seven Deadly Annoyances of API Design
API 設計是一門藝術,也是一門科學,很不幸,很多聰明的人都沒能做好這件事兒。之所以如此,是因為他們把「給開發者帶來煩惱」當成了 API 設計的第一要務。
我的兄弟姐妹們,就讓我們聚在一起,「枚舉」一下 API 設計的七宗罪吧。我從未想到自己竟然也會寫出這種列表類的文章,但好在我這篇文章的名字有著深刻的宗教意味。
首先,先明確幾點:我們這裡討論的是那些成功的,工作得很好的 API。因此,類似「根本無法用」和「有巨大的安全漏洞」和沒人用的這類 API 不在我們的討論之列。不要被標題中的「極度」給嚇到了,誠然,根本沒法用的 API 非常的失敗,但也並沒有讓開發者極度惱怒的效果。我們這裡討論的是可以工作的那些 API 的一些小細節,那些眾所周知的,令人不忍直視的,讓開發者恨不得拽頭髮、摔鍵盤、爆粗口的,小細節。
文章中要講到的很多例子都來自於安卓,我並不是對安卓有著特別的挑剔,僅僅是因為這個開發環境最近經常惹惱我而已,很多其他的開發環境或多或少地,也有類似的問題。
煩惱一:API 返回里有個 key 叫 「trsnss」……
是的,我沒有故意打錯字。不得不承認,命名這件事非常困難,但是,一些基本常識可以解決命名中的大部分問題。我們應該在 Unix 那幫人為了省一次敲擊鍵盤的英雄壯舉里得到點兒啟發——《如果 dupe 精簡成 dup 這個名字,我們為什麼還要多打一個 E 呢?》
第二條跟第一條有點關係。
煩惱二:這種函數名不陌生吧,makingNamesMuchTooLongToBePractical
類似的,蘋果公司給開發者提供了這麼一個函數名:
outputImageProviderFromBufferWithPixelFormat:pixelsWide:pixelsHigh:baseAddress:bytesPerRow:releaseCallback:releaseContext:colorSpace:shouldColorMatch:
煩惱三:濫用別名
嗯,安卓,說得就是你。
顯然, 「BlankActivity」 和 「EmptyActivity」 這倆函數應該是有所不同的。但不同在哪兒呢?我曾經很清楚,但現在我的腦袋裡空空如也(empty),我的意思是,我的大腦一片空白(blank)。
安卓給開發者提供了 tool bar,後來被重命名成了 app bar,還可以叫 action bar,因此我們寫出下面的代碼也就不足為奇了:
// Set the toolbar as my app barsetSupportActionBar(myToolbar);
精簡還是冗餘?顯然 google 沒有考慮這個問題。「嗯,為什麼不兩個都保留呢?」,因此,同時出現 getExternalDirectory 和 getExternalFilesDir 這兩個函數就不足為奇了。順便告訴大家,這倆函數返回的是不同的地址信息,因為有時候我們會在目錄里存文件,另一些時候會存...呃,其實我也不知道目錄裡面除了文件還能存啥…
還有,哪個手機開發者能分得清 tap 和 touch 操作的區別?我反正是搞不清楚,而且設計安卓 API 的人也搞不清楚,因為他們有時候也管這個操作叫 click ……
嘿,你的開發機是什麼系統?哦,KitKat?安卓 4.4?還是 SDK 19 版本?呃,等一下,其實這三個說得是同一個意思,KitKat 就是安卓 4.4,也是 SDK 19 版本......寫到這裡我只能祝你們好運了。
好吧,再說最後一個安卓的例子吧。想要通過 URL 來訪問項目的 asset 資源嗎?這還不簡單,只需要把資源放在源碼里的 /assets/ 文件夾下,然後通過路徑 file:///android_asset/ 來引用就可以了!是的,你沒有看錯,文件夾命名里的 asset 是複數,而引用路徑里的 asset 卻是單數!並且路徑里的 asset 前面還緊跟著一個下劃線呢!為什麼要這麼干?因為人家是 Google,人家想怎麼干就怎麼干。你除了忍受你還能怎麼辦呢?總不能轉向 FirefoxOS 開發吧?
煩惱四:簡陋的(甚至沒有)文檔
說到上面提到的 asset URL 的命名問題,其實這個問題你看文檔的話是發現不了的,這個問題只在一個叫做 WebSettings.setAllowFileAccess 函數的里有所提及,而這個函數跟 asset 的使用又沒有什麼關係。是的,這就是傳說中的欠缺文檔說明的 feature。
還有 Context 這個類,這個類在代碼里到處都是,但沒人能確知這個類是幹嘛的。幾乎所有的 API 調用都用到它,但沒人知道為什麼要用它。這個類和開發者們就這麼相安無事,直到現在。
想在你的 Activity 里加一個 ToolBar/ActionBar/AppBar 嗎?你可以用這個向後兼容的庫做這件事。什麼,你不想向後兼容?嗯,我沒有向後兼容的需求,可以不用這個庫就把這事兒做掉嗎?也許有吧,反正在官方文檔里你是找不到的。
說到向後兼容......
煩惱 5.1 beta 3:版本不兼容
如果你是安卓開發者,你應該知道我在講什麼……我簡直寫不下去了,這種事兒回憶起來,簡直太TM痛苦了,你懂的。
為什麼踏馬的可以在 KitKat 上跑但在 MarshMallow 和 JellyBean 上一跑就掛?!啊?啊?你告訴我?為!什!!么!!!
煩惱 6:簡單的事情複雜化。
嗯,Fragments,我對你恨得特別起勁。嗯,直接看下節選自 Android 官方文檔的一張介紹 Fragments 生命周期的圖你就懂了:
但比 Fragments 更糟糕的是 Notifications。你去翻一下官方文檔中的「創建簡單的通知」這一章就知道了。為了告訴你這個通知實現起來是多麼的簡單,文檔緊接著重複了一次標題的內容:「下面這個例子展示了如何創建一個簡單的通知」。嗯,想看下這段簡單的代碼嗎?嗯,就是這段:
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.notification_icon) .setContentTitle("My notification") .setContentText("Hello World!");// Creates an explicit intent for an Activity in your appIntent resultIntent = new Intent(this, ResultActivity.class); // The stack builder object will contain an artificial back stack for the// started Activity.// This ensures that navigating backward from the Activity leads out of// your application to the Home screen.TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);// Adds the back stack for the Intent (but not the Intent itself)stackBuilder.addParentStack(ResultActivity.class);// Adds the Intent that starts the Activity to the top of the stackstackBuilder.addNextIntent(resultIntent);PendingIntent resultPendingIntent = stackBuilder.getPendingIntent( 0, PendingIntent.FLAG_UPDATE_CURRENT );mBuilder.setContentIntent(resultPendingIntent);NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);// mId allows you to update the notification later on.mNotificationManager.notify(mId, mBuilder.build());
我的天,我簡直被上面這 27 行簡潔的代碼所折服!
緊接著,在這段代碼的後面,文檔寫道:「就這麼簡單,你的用戶就收到了通知」。哇哦,好簡單哦,就在我準備寫上一大坨代碼的時候,你給了我這 27 行清晰簡潔漂亮的代碼,我簡直鬆了好大一口氣。對了,別忘了 import 這段代碼需要的 3 個類,還有,別忘了把依賴的 gradle 文件放進去,還有...... 我選擇狗帶,我去酒吧當服務員去了,我不寫代碼了。
什麼?
等等,還有,別忘了把一個 30 行的 xml 拷貝到另外的一個文件中去。
說到這裡,我們的第七個煩惱就是......
煩惱七:XML
呵呵,XML,我不想說了。
推薦閱讀:
※[Android] Toast問題深度剖析(一)
※API安全架構之抽絲剝繭
※阿里雲SDK再升級,宣布支持C++語言
※Web API介面如何防止本網站/APP以外的調用?
※API 性能優化(一)