如何從 Parse 手中拯救你的應用

做者:Reinder de Vries,原文連接,原文日期:2016/01/09
譯者:pucca601;校對:靛青K;定稿:numbbbbb前端

編者注:這篇文章是由 Reinder de Vries 撰寫,一個在 LearnAppMaking.com 的獨立應用開發者。web

喔不! Parse 即將關閉...你打算怎麼辦呢?

今年 1 月 28 號,Parse 宣佈它將會關閉本身的服務。他們正在慢慢減小已提供的「後端服務」,打算在 2017 年 11 月 28 日全面撤出該服務。編程

對於從 2011 年開始就依賴該服務的超過 500,000 的開發者來講這真是個壞消息。正如 Jameson 在他的博客帖子中指出的同樣,開發者們感受被他們的信任背叛了。swift

Parse 在 2013 年被 Facebook 以八千五百萬美圓收購,但如今「五巨頭」之首冷血地切斷了對開發者們應用的支撐服務。後端

在 2013 年,收購 Parse 其實有不小的風險,可是當時 Facebook 須要在移動市場立足,因此不得不買。Facebook 最新的收益報表顯示,2016 年 Q4 的移動廣告相比 2014 和 2015 年增加 52%,這代表他們已經在移動市場站穩了腳跟。安全

Amazon、Microsoft 和 Google 的雲服務競爭愈來愈激烈,你可能以爲 Facebook 再也不須要 Parse 。從商業的角度我是十分贊同的,但從開發者視角這是一個糟糕的決定。服務器

真的是這樣嗎?app

對開發者來講, Parse 的關閉是一個機會

在我作應用以前,我一直在開發網站。不可勝數的網站。我學會了全部必備技能:HTML、CSS、JavaScript、PHP 和 MySQL。個人代碼質量和優雅程度在當時會讓你起雞皮疙瘩,可是經過不斷地試錯,我最終掌握了輸出高質量網站的技術。框架

年復一年,我時不時會開發一個 web 應用來保持技藝與時俱進。有許多更新更先進的工具出現,我在工做中也能夠應用它們。我從 jQuery 遷移到 Angular 再到 React,期間接觸了太多 JavaScript 框架,見證包管理工具以及 GitHub(SourceForge,還有誰?)的出現,還有 PHP 4 到 PHP 5 再到 PHP 5.6 以及即未來臨的 PHP 7。個人 MySQL 表格撐爆了,因此我切換到 Elasticsearch 和 Mongo。編程語言

不久前我想要自動化時訊郵件註冊和 TestFlight 的測試邀請。若是你訂閱時訊而且確認了訂閱,這個自動化流程會自動將你加入 TestFlight 的測試組。我對 FastLane 工具以及它的 boarding 組件有必定了解,這個圖形化 boarding 組件的 HTML 頁面必定有一個我能劫持的 POST URL 入口點。實際上,我會從個人表格中抓取訂閱者的郵箱地址並將其放入 boarding 表格。Boarding 會完成剩下的事情,好比將訂閱者添加到 TestFlight 的測試組。

這裏只有一個問題: boarding 表格有 CSRF 保護,這意味着它須要每一個請求裏包含一個獨一無二的令牌。這些令牌是服務端生成的,在個人自動化流程裏無法獲取它們。

我能訪問 boarding 的源代碼,因此理論上我可讓 CSRF 保護失效,從而解決這個問題。只有一點要先聲明:源代碼是 Ruby 寫的,可是我在有生之年沒有看過一行 Ruby 代碼(好吧,也許有一行),更不用提寫了。

多虧我有大量後端編程經驗,我不懼怕上手一門不熟悉的技術。經年累月的反覆試驗和犯錯讓我對 web 如何運做有深刻的瞭解。我沒有羞於接觸這個一無所知的技術,相反,我挖得很深刻。

我翻閱了各類沒有見過的庫,以及它們相比之下很奇怪的名字(這算個事兒嗎?)後,終於找到了管理 CSRF 保護特性的文件。最終我發現,關掉這個特性至關於將 controller 置空。

後端黑盒

幾乎每個成功的應用多少都會使用一種後端服務來鏈接用戶,將用戶數據在服務器上持久化。

不少我遇到和指導過的開發者對後端編程缺少紮實的理解。他們在極其簡單的 Dashboard 上設置 Parse 的配置項,寫一個查詢語句,而後就認爲數據「就在那裏」。誰會想要知道雲端發生了什麼呢?

我經常將 Parse 解釋爲「應用的電子表格」。這是事實:Parse 爲你的應用提供了後臺服務,它的 Dashboard 和 Google Spreadsheets 同樣簡單。你甚至不須要寫查詢數據的語句,相反,你可使用下面這樣簡單的方法:

PFQuery(className: "Tweet").whereKey("user", currentUser);

這或許也是 Parse 的錯。它們的 PFQueryTableViewController 用起來實在太簡單,可是也至關危險:它直接將後端代碼與你應用的 view controller(已是個 ViewModel)關聯了。即便你想從 Parse 轉到 Firebase,由於你的代碼和後端緊密耦合,你不得不在遷移以前重寫整個應用。

Parse 的 PFObject 類內置了映射,因此你能夠像使用原生的 Swift String 對象同樣直接使用字符串數據, Parse SDK 會在內部將這些數據轉化成能夠在互聯網上輸送的數據。若是你在面向用戶的代碼中使用了 PFObject 實例,那麼將來要替換一套新的「後端即服務」 SDK 會很難。

若是你要從頭構建一個本身的後端,你須要知道例如 true(一個布爾值)在編碼爲 JSON 的時候會轉化爲 true (一個字符串)。若是那個 JSON 數據抵達你的 app,你須要處理的是一個 String 實例而不是你指望的 Bool 類型。雖然 Swift 有一個難以置信的類型系統,可是 if let 可選類型綁定狀態判斷機制(optional binding statement)沒法處理不匹配的類型——它並不能解決這個問題!

因此 Parse 的關閉對你有什麼好處?這是又一次開始試驗的好機會,你能夠深刻挖掘後端黑盒,保護你的代碼再也不遇到相似的事情。

三種應對方法

下次你要開發應用時,請將下面這三個方法記在腦子裏。你能夠用它們保護你本身(的業務)以防 Parse 關閉這樣的事情再次發生。不要 100% 信任一個對你應用持續發展和成功相當重要的第三方服務提供商。

1. 列出一個替代品清單

Parse 有幾個類似的提供商,好比 Firebase。可能在 Parse 關閉以前你都沒據說過 Firebase。

最酷的事是:大多數這類服務知道你正在尋找它們。若是你 Google 「parse shutdown(譯者注:Parse 關閉)」 或者 「parse migration(譯者注:Parse 遷移)」,你會正中許多高度相關的 Google AdWords 廣告,叫你遷移至它們服務的廣告。千萬別這麼作!

相反,你應該列一個替代品清單。儘可能熟悉外面的東西。列一個你須要依賴的服務商以及替代品清單,以防萬一。就像超級市場會有備用牛奶供應商的清單以防他們主要的供應商破產,你也應該擁有一份和你有業務往來的公司清單。

固然,你還能夠對清單上的替代供應商作一些和你相關的技術和 SDK 研究。我以前徹底沒據說過 OneSignal,直到我開始尋找一個替代 Urban Airship 的免費推送供應商,由於我以爲 Urban Airship 的費用太貴了。

2. 解耦你的代碼

雖然你已經聽了一萬遍:Model-View-Controller(譯者注:模型-視圖-控制器)仍是有道理的。咱們正漸漸習慣 Model-View-Whatever(譯者注:模型-視圖-任何東西),可是 MVC 的理念實際上是:你的 Controller 不能配置 View,你的 View 不該該操縱一個 Model。

Cocoa Touch 框架讓人很難不使用 MVVM(Model-View-ViewModel),可是這不意味着你要將你的 Model 代碼與 View Controller 代碼緊耦合。

在編程理論中咱們有兩條原則:耦合與封裝。使用兩個緊耦合類意味着 ClassA 做出了不少關於 ClassB 內部功能的假設。

想象一輛車和一位司機。司機坐在車裏的前方位置,由於駕駛杆是與引擎直接相連的,若是司機忽然坐到車後方來駕駛呢?這樣確定行不通。固然,相似這樣的狀況在現實生活裏永遠不會發生,可是這種狀況在你的代碼裏一直存在。

應對緊耦合的一個保護措施就是封裝。這意味着每個類有着一個嚴格定義的方法集合,好比 speedUp()steerLeft()。經過依賴可燃的引擎,你只須要讓司機調用 speedUp(),而不是讓司機將更多的汽油注入燃燒室。用這種方式司機不須要知道有關引擎如何工做的複雜知識,只須要調用一個他知道安全的方法就能實現了。

當這種引擎被一個電動的不帶燃燒室的引擎所替代,你也不須要修改司機的代碼:他依賴一個暴露的 speedUp() 方法,而不是依賴 injectGasMixture()gasMixturesparkTimer(譯者注:注射混合汽油,混合汽油,電火花計時器)。

若是你以爲解耦和封裝難以理解,就將它們看做職責吧。誰的職責是駕駛?司機。誰的職責是使汽車向前移動?引擎。

當你正在編寫一個有後端能力的應用時,要清楚地定義組件以及組件的職責。建立一個在後端代碼和前端代碼之間只起媒介做用的中間件。前端代碼基於你本身的類構建,就像 Car,後端代碼處理你用到的後端 SDK 而且使用相似 PFObject 的類。

中間件的工做是溝通前端和後端。假使你的後端代碼過期,你能夠替換中間件的代碼讓它使用新的後端 SDK,無需重寫前端代碼。

3. 使用 Adapter Pattern(適配器模式)

第三種也是最後一種方法是減小代碼耦合。Adapter Pattern(適配器模式),比互聯網自身還要老,是一個面向對象編程的模式。這是一套編程設計規則,能夠用它來將兩個不相容的類變得相容。

這相似於構造一個「 wrapper(封裝器)」,不僅是暴露資源接口(像是封裝一個 REST API ),它還會在兩個不相容接口間制定一套規則。在不少編程語言裏這些規則叫接口(JAVA 和 PHP),在 Swift 和 Objective-C 中它們叫 protocols(協議)。

你必定知道 Swift 的範型,對嗎?若是有個東西遊起來和叫起來像一隻鴨子,那麼它必定是隻鴨子。Adapter Pattern(適配器模式)也有相似機制。要讓 ClassA 和 ClassB 解除耦合,你須要構造一個 ClassA 的適配器,而不是直接在 ClassA 裏調用 ClassB。

這個適配器類包含有關 ClassA 如何工做的複雜知識。適配器類也遵循一個協議。這個協議說:「你能夠肯定我有 X,Y 和 Z 方法」。

ClassB 不知道 ClassA。它只知道它想要一個遵循適配器協議的實例。它不在意這個實例是否是個鴨子,只要它遊起來叫起來像個鴨子就好了。

使用適配器有什麼好處?假設你原來的 ClassA 代碼廢棄了,你只須要找另外一個類(例如另外一個 Parse 的替代品)而且爲這個類寫一個適配器。只要這個適配器遵循協議,也就是你以前制定的規則,ClassB 依舊可以工做。

事實上,你能夠寫一個帶有下面這個簽名的方法的適配器(MyTwitterAdapter):

class MyTwitterAdapter:AnyTwitterServiceProtocol
{
    func getTweetsWithUser(user:User, withPage page:Int = 0)
    {
        ...

協議(AnyTwitterServiceProtocol)包含了與上面如出一轍的方法簽名,可是它沒有任何邏輯或者方法實現,只是定義了規則。

protocol AnyTwitterServiceProtocol
{
    func getTweetsWithUser(user:User, withPage page:Int = 0);
    ...

前端代碼(MyTweetsViewController)有一個未定義類型的變量 twitter,這個工廠方法能夠保證當你更換適配器的時候你只須要替換類的名字一次(或兩次)。

若是你將 twitter 類型嚴格定義爲 MyTwitterAdapter,你的代碼中可能還有不少耦合處。這樣的話,若是你要將適配器替換爲一個新的適配器,仍是須要更新大量代碼。

一旦你設立了這段檢查機制,就能夠測試這個你正在工做的實例 twitter 是否遵循 AnyTwitterServiceProtocol

if let _twitter = twitter as? AnyTwitterServiceProtocol
{
    // Yes, it conforms
}

你的前端代碼不再用關心 twitter 是不是 CarFooBarFacebook 類型,只要它遵循了 AnyTwitterServiceProtocol 就行。它只想知道這個實例是否有一個方法叫 getTweetsWithUser:withPage:

注:假設你真的須要嚴格定義 twitter 的類型,可使用協議做爲類型,這樣你也能使用泛型。

接下來怎麼作?

如今你已經學會了怎麼改善業務代碼、怎麼處理你依賴的供應商以及保護你的代碼,我想你已經明白怎麼應對相似的危機了。

你是否是正要轉到 Firebase ?你應該知道 Firebase 屬於「五巨頭」中的 Google。雖然 Parse 關閉不意味着 Firebase 也會關閉,但依賴一個新的供應商會讓你的應用和以前同樣脆弱,除非你想辦法保護你的代碼。

學習後端編程是一個好辦法。Parse 已經發布了一個優秀的替代產品,Parse Server,你能夠在其上運行你本身的服務器(只要它能夠運行 NodeJS 和 MongoDB)。在下面的文章中我會向你展現如何將一個簡單的應用(包括 Heroku 和 MongoLab)從 Parse 遷移至 Parse Server。

繼續閱讀:指南: 如何將一個簡單的應用(包括 Heroku 和 MongoLab)從 Parse 遷移至 Parse Server

另外一個相對容易的練習是將你的推送通知部署到兩個服務中,不要把雞蛋放在一個籃子裏。實現推送服務供應商的客戶端 SDK 相對來講比較容易,一般你只須要發送惟一的 ID 並創建一個段。

使用 Adapter Pattern(適配器模式)去建立一個統一的接口,供你的前端使用。經過定義一個協議將實際的適配器從前端代碼解耦。挑選一個備用的推送通知服務供應商(好比 Urban Airship, Mixpanel, Pusher 或 PushOver),寫一個備用的適配器。確保這個適配器遵循這個協議,而後模擬一下服務崩潰,看看如何從新部署適配器。那麼,你的問題解決了嗎?

結論

不要懼怕深刻黑盒。寫你本身的後端 REST API 是個好辦法,放手去作,你會學到不少東西。

你知道 Crashlytics 屬於 Twitter 嗎?任何服務均可能會關閉……搞清楚 Crashlytics 爲你作了什麼,以及它的工做原理。這是創建一個穩定可靠而且能一直賺錢的業務的惟一途徑。

接下來,你要本身走進兔子洞了。

關於做者

Reinder de Vries 是一個獨立應用開發者,他在 LearnAppMaking.com 上教有志向的開發者以及市場營銷者如何構建他們的應用。他已經開發了 50 多個應用,他的代碼被全世界成千上萬的用戶使用。不寫代碼的時候,他喜歡濃縮咖啡(strong espresso)和旅行。

本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg

相關文章
相關標籤/搜索