保羅的學習筆記

Day 21 - 避免讓資料源綁架程式

不管是前端或是後端,我們總有許許多多時候會需要存取外部資源,以 App 來說,大多時候我們都會需要呼叫外部 API,獲取當前頁面所需要的資料。外部 API 的種類則有許多形式,例如最常見的 Web API、各種 Firebase 套件提供的方法,或者是 sqlite。

舉個例子

假設我們正在寫一個顯示最新新聞的 App,在 App 被打開時,App 會使用後端 API 撈取最新幾筆的新聞,並顯示在畫面上。

Article image

上面的程式碼乍看之下好像沒什麼問題,但是仔細想想就會發現,這段程式違反了 DIP 原則。我們讀取資料的邏輯緊緊地與呼叫外部 API 程式碼綁在一起。其中造成許多問題,而其中一個問題是,當我們想要改成資料從 sqlite 讀取時,由於 sqlite 類別有著不同的方法,我們就會被迫修改 initState 中的呼叫。

Article image

與第一段程式碼相比,我們不只把 HttpDataSource 換成了 SqliteDataSource,也把呼叫的方法換成 queryNews 了。

用 Repository 改善

為了解決這個問題,我們可以引入 Repository Pattern 來處理。定義一個 NewsRepository 介面,在介面中宣告存取資料的方法,並透過給定實作類別,決定到底是要打 Web API 拿資料,或者是從 Sqlite 讀取。

Repository:Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.

Article image

透過實作這個模式,當我們想換實作時,只在修改實作的類別,就可以輕鬆切換。因為使用的地方是依照介面來呼叫的,所以當不同資料源的類別都實作相同介面,我們就不用修改其他程式碼,只要切換實作類別即可。

用狀態管理轉接

在上面的例子中,我們都是直接在 Widget 中使用 Repositroy,但是這種為了讓例子變得簡潔的作法。在真實情況中,大多時候我們會使用狀態管理套件去存取 Repository,當狀態管理套件取得資料時,再通知 Widget 更新畫面。通常我們不會讓 Widget 直接存取 Repository。

若用 flutter_bloc 來修改上面的例子,我們建立了一個 NewsCubit,並在裡頭使用 NewsRepository 這個介面去存取資料,然後再建構子決定使用哪種 DataSource。

Article image

最後則是畫面會監聽 NewsCubit 的狀態,並依據狀態即時更新畫面,顯示新聞

Article image

利用以上作法,當我們想抽換 Repository 實作時,只要到 NewsCubit 中修改即可,至於使用 NewsRepository 的地方無需修改。

結論

其實 Repository 被廣泛運用在各類應用程式中,不只是 Flutter,像是 Android 官方推薦的架構中,也可以看到 Repository 身影,透過墊了一層 Repository 隔開了商業邏輯與資料存取。

— 全文完 —

如果這篇對你有幫助,可以分享給朋友,或追蹤後續更新。我在 Threads 也會貼一些零碎想法。