dcache-android框架中的設計模式詳解

引言:孤獨的人喜歡深夜,多情的人喜歡黃昏。幸福的人喜歡陽光,傷心的人偏愛風雨。

眾所周知,dcache-android是本人一行一行代碼手寫出來的Android數據緩存框架,寫了好幾年了,雖然不是每天寫,但一直在持續優化中。先放個Github地址https://github.com/dora4/dcache-android ,歡迎watch+star+fork+follow四連,這是對我的肯定也是給我持續優化的動力。

寫本文的時候,dcache-android框架的最新版本為2.2.6版本,分析源代碼不提版本那不就是誤人子弟?雖然在框架設計暴露出的低層模塊API的時候,就考慮到了關鍵代碼的變動要給開發者時間消化。但是高層模塊,也就是很多類都依賴的(如頂層接口設計)的變動,在不同版本還是變化很大的。

抽象工廠模式

在最新的版本更新中,加入了MMKV簡單數據的Repository的支持,原先的BaseRepository的設計中有如下代碼。

/*** 非集合數據緩存接口。*/
protected lateinit var cacheHolder: CacheHolder<M>/*** 集合數據緩存接口。*/
protected lateinit var listCacheHolder: CacheHolder<MutableList<M>>

因為之前只考慮了數據庫緩存,所以也沒有使用工廠模式。但是隨著架構逐漸變得復雜,這一類問題其實可以使用抽象工廠模式對代碼進行擴展。抽象工廠簡單的來說,是工廠方法模式的升級版。工廠方法模式的工廠一個工廠接口只創建一種產品,而抽象工廠的工廠接口,可以創建多種類型的產品。就好比,手機卡不僅能打電話,還能發短信、流量上網。現在你可以制定一個套餐,每種套餐每月有多少電話通話時長、可以發多少條短信、多少G流量。一種套餐就是一個抽象工廠的實現類。這樣用一個詞來形容,就是產品簇。組裝電腦是不是也是一個產品簇?你可以使用因特爾的處理器、西部數據的硬盤、羅技的鼠標鍵盤、英偉達的顯卡、華碩的主板。咳咳,我先聲明一點,以上內容沒有任何形式的商業合作。好了,言歸正傳。在你設計一個系統的時候,如果未來有幾個模塊是必須有的,而且可能會有不同的實現組合,你可以考慮使用抽象工廠模式。

那么BaseRepository就變成了這樣。

abstract class BaseRepository<M, F : CacheHolderFactory<M>>(val context: Context) : ViewModel(), IDataFetcher<M>, IListDataFetcher<M> {
}

然后通過數據庫工廠創建它的集合與非集合模式的CacheHolder,MMKV亦然,如果以后有了新的緩存類型,也可以通過同樣的方式擴展。

適配器模式

在dcache-android的源碼中,有個地方使用到了適配器模式,
dora.cache.data.adapter.Resultdora.cache.data.adapter.ResultAdapter。接下來看一下ResultAdapter的源代碼。

package dora.cache.data.adapterimport dora.http.DoraCallback/*** 將實現[dora.cache.data.adapter.Result]的REST API接口返回的model數據適配成框架需要的* [dora.http.DoraCallback]對象,用于[dora.cache.repository.BaseRepository]的onLoadFromNetwork()中。** @see ListResultAdapter*/
open class ResultAdapter<M, R : Result<M>>(private val callback: DoraCallback<M>) : DoraCallback<R>() {/*** 適配[dora.http.DoraCallback]成功的回調。*/override fun onSuccess(model: R) {model.getRealModel()?.let { callback.onSuccess(it) }}/*** 適配[dora.http.DoraCallback]失敗的回調。*/override fun onFailure(msg: String) {callback.onFailure(msg)}
}

因為后端REST API接口設計的差異,可能不會直接返回的頂層數據類就是我們要完整緩存的數據。通常會有code、msg以及data的設計。那我們默認肯定是要支持直接緩存頂層數據類的,那么問題來了,要緩存的數據類不在最外層,比如是頂層數據類的一個成員屬性。而且舊的接口也是不能刪除的,不可能說我新增一個功能,把舊的改掉了。而我系統的其他地方又是非常好的設計,不想改,然后要把新舊模塊都接入進來。有沒有一種辦法,在不改變原有系統設計和不放棄舊接口的情況下,讓新接口也能夠接入到原有系統設計上?肯定是有的。排插就是最好的例子。墻上的插口和我電器的接口不一致,重新裝修的成本又太高,那怎么辦?買一個排插,新舊的電器都可以正常使用了。

訪問者模式

訪問者模式可能有些開發者用得不是很多,它是一個保證不破壞數據原有結構的情況下,對樣本數據進行抽樣訪問的一個設計模式。VIP跟普通用戶的訪問數據是不是有可能不一樣,訪問者模式也可以方便進行權限限制。你老板是不是可以訪問你的薪資,財務是不是也可以訪問你的薪資,你的領導是不是還有可能能訪問你的薪資,但是你同事不行。設計模式來源于工業生產和生活,哈哈。下面看代碼。

package dora.cache.data.visitorimport dora.cache.data.page.IDataPager/*** 分頁數據的訪問者,不破壞數據的原有結構,訪問數據。** @param <M>*/
interface IPageDataVisitor<M> {/*** 訪問數據分頁器。** @param pager*/fun visitDataPager(pager: IDataPager<M>)/*** 過濾出符合要求的一頁數據。** @param model        樣本數據* @param totalCount  數據總條數* @param currentPage 當前第幾頁* @param pageSize    每頁數據條數* @return 該頁的數據*/fun filterPageData(models: MutableList<M>, totalCount: Int, currentPage: Int, pageSize: Int): MutableList<M>
}

在dcache-android中,也有使用到訪問者模式。

package dora.cache.data.pageimport dora.cache.data.visitor.IPageDataVisitor/*** 數據分頁器,使用訪問者進行訪問。** @see IPageDataVisitor*/
interface IDataPager<M> {/*** 設置當前是第幾頁,建議從0開始。*/var currentPage: Int/*** 每頁有幾條數據?** @return 不要返回0,0不能做除數*/var pageSize: Intval models: MutableList<M>/*** 加載過濾后的頁面數據。*/fun loadData(models: MutableList<M>)/*** 頁面數據改變后,會回調它。*/fun onResult(result: (models: MutableList<M>) -> Unit) : IDataPager<M>/*** 接收具體訪問者的訪問,不同的訪問者將會以不同的規則呈現頁面數據。*/fun accept(visitor: IPageDataVisitor<M>)
}

在數據分頁器中接受訪問者的訪問。這里有兩種默認的訪問者實現,一種直接分頁,還有一種是隨機分頁。

package dora.cache.data.visitor/*** 默認的數據分頁器。*/
class DefaultPageDataVisitor<M> : BasePageDataVisitor<M>() {override fun filterPageData(models: MutableList<M>, totalCount: Int, currentPage: Int, pageSize: Int): MutableList<M> {val result: MutableList<M> = arrayListOf()val pageCount = if (totalCount % pageSize == 0) totalCount / pageSize else totalCount / pageSize + 1for (i in 0 until pageCount) {result.add(models[currentPage * pageSize + i])}return result}
}
package dora.cache.data.visitorimport kotlin.random.Random/*** 從樣本數據中隨機讀取數據的數據分頁器,不保證去重。*/
class RandomPageDataVisitor<M> : BasePageDataVisitor<M>() {override fun filterPageData(models: MutableList<M>, totalCount: Int, currentPage: Int, pageSize: Int): MutableList<M> {val result: MutableList<M> = arrayListOf()val pageCount = if (totalCount % pageSize == 0) totalCount / pageSize else totalCount / pageSize + 1for (i in 0 until pageCount) {result.add(models[Random.nextInt(totalCount)])}return result}
}

你也可以根據實際的業務需求,來擴展自己的訪問者。因為我不想你讀取數據還要改我dcache-android框架的代碼,所以設計了此訪問者結構。數據分頁器綁定onResult回調,一旦調用accept方法,接受某個訪問者的訪問,數據就會自動回調到onResult。這樣也遵循了最小知識原則。如果你對框架的緩存機制不感興趣,你只需要自己實現訪問者。然后框架給你所有的緩存數據,你自己處理就好了,不用再細讀源碼。

總結

設計模式只是為了設計出更好擴展的系統,并不是非得為了使用設計模式而使用設計模式,具體還要看業務,有沒有這個使用必要。當然開源框架本來就是給別人用的,所以設計模式用得比較多。架構設計的精髓不在于硬套設計模式進行設計,而是你設計得足夠多了以后,不去硬性使用設計模式,而設計模式無處不在。這樣你的架構設計能力就達到了一個新的境界了,設計模式你已經能完全掌控了。它已經融入到了你的骨髓,不是嗎?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/13832.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13832.shtml
英文地址,請注明出處:http://en.pswp.cn/web/13832.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【go從入門到精通】精通并發編程-使用扇入扇出提升多個通道之間傳遞數據的效率

在并發編程領域,Golang 作為一種擅長處理并發的編程語言而脫穎而出。 Go 并發模型的一個關鍵組件是通道,它允許 goroutine 進行通信并同步其工作。在這里,我們將探討在 Go 中的多個通道之間傳遞數據的技術。當需要協調不同 goroutine 之間的工作并管理數據流時,這非常有用。…

TypeScript-類型斷言

類型斷言 當開發者比TS本身更清楚當前的類型是什么&#xff0c;可以使用斷言(as)讓類型更加精確和具體 const _link document.getElementById(link) console.log(_link.href) // 出錯了&#xff0c;如下圖 const _link document.getElementById(link) as HTMLAnchorElement…

【三數之和】python,排序+雙指針

暴力搜索3次方的時間復雜度&#xff0c;大抵超時 遇到不會先排序 排序雙指針 上題解 照做 class Solution:def threeSum(self, nums: List[int]) -> List[List[int]]:res[]nlen(nums)#排序降低復雜度nums.sort()k0#留兩個位置給雙指針i,jfor k in range(n-2):if nums[k]…

【再探】Java—泛型

Java 泛型本質是參數化類型&#xff0c;可以用在類、接口和方法的創建中。 1 “擦除式”泛型 Java的“擦除式”的泛型實現一直受到開發者的詬病。 “擦除式”的實現幾乎只需要在Javac編譯器上做出改進即可&#xff0c;不要改動字節碼、虛擬機&#xff0c;也保證了以前沒有使…

光伏電站在線監測智能診斷系統:開啟無人值守新紀元

光伏電站在線監測智能診斷系統&#xff1a;開啟無人值守新紀元 大家都知道光伏電站是通過汲取著太陽的光芒&#xff0c;為人類提供源源不斷的電能源。然而&#xff0c;隨著光伏電站規模的擴大和復雜性的增加&#xff0c;如何有效提高發電效率、減少人工維護成本&#xff0c;實…

YOLOV5算法多目標檢測系統

歡迎大家點贊、收藏、關注、評論啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代碼。 文章目錄 一項目簡介 二、功能三、系統四. 總結 一項目簡介 一、項目背景與意義 隨著計算機視覺技術的飛速發展&#xff0c;目標檢測已成為許多實際應用場景中的關鍵技術&…

AWS存儲之 Storage Gateway

AWS Storage Gateway是一項混合存儲服務&#xff0c;它允許您在本地環境和AWS云之間無縫地集成存儲解決方案。它提供了一種簡單、安全地方式&#xff0c;讓您可以將本地應用程序連接到云存儲服務&#xff0c;如Amazon S3、Amazon Glacier、Amazon EBS等。 比如一個公司如果想將…

數據結構之二叉樹的超詳細講解(2)--(堆的概念和結構的實現,堆排序和堆排序的應用)

個人主頁&#xff1a;C忠實粉絲 歡迎 點贊&#x1f44d; 收藏? 留言? 加關注&#x1f493;本文由 C忠實粉絲 原創 數據結構之二叉樹的超詳細講解(2)--(堆的概念和結構的實現,堆排序和堆排序的應用) 收錄于專欄【數據結構初階】 本專欄旨在分享學習數據結構學習的一點學習筆記…

電腦卸載linux安裝windows后每次開機都出現grub

原因分析 這是因為電腦硬盤中還存在linux系統的引導程序&#xff0c;并且啟動順序還在windows之前&#xff0c;有時候通過bios根本找不到它的存在&#xff0c;以至于每次windows開機出現grub之后都要輸入exit退出linux的引導之后才能使得電腦進入windows&#xff0c;這個有時會…

算法訓練營第三十六天 | LeetCode 1005 K次取反后最大化的數組、LeetCode 134 加油站

LeetCode 1005 K次組飯后最大化的數組 這題貪的主要是數值最大化。如果K > 負數個數&#xff0c;我們就先將負數全部轉換成它的相反數&#xff0c;并將K--&#xff0c;之后K剩余的值可以對2取模&#xff0c;為0的話直接得出最后結果&#xff0c;為的話我們要在當前所有值里…

Python | Leetcode Python題解之第108題將有序數組轉換為二叉搜索樹

題目&#xff1a; 題解&#xff1a; class Solution:def sortedArrayToBST(self, nums: List[int]) -> TreeNode:def helper(left, right):if left > right:return None# 選擇任意一個中間位置數字作為根節點mid (left right randint(0, 1)) // 2root TreeNode(nums…

純血鴻蒙APP實戰開發——邊緩存邊播放案例

介紹 OhosVideoCache是一個支持邊播放邊緩存的庫&#xff0c;只需要將音視頻的url傳遞給OhosVideoCache處理之后再設置給播放器&#xff0c; OhosVideoCache就可以一邊下載音視頻數據并保存在本地&#xff0c;一邊讀取本地緩存返回給播放器&#xff0c;使用者無需進行其他操作…

NDIS小端口驅動(五)

在需要的時候&#xff0c;我們也許需要NDIS微型端口程序信息&#xff0c;下面會從多個方面來討論如何查詢NDIS微型端口驅動。 查詢無連接微型端口驅動程序 若要查詢無連接微型端口驅動程序維護的 OID&#xff0c;綁定協議調用 NdisOidRequest 并傳遞 一個NDIS_OID_REQUEST 結…

Mac 安裝 git

文章目錄 前言一、介紹二、下載三、驗證四、配置五、Git常用命令六、git提交和撤銷工作流程代碼提交和提交同步代碼撤銷和撤銷同步 FAQ1.homebrew 下載解決方法一&#xff08;強烈推薦&#xff09;&#xff1a;解決方法二&#xff1a; 總結 前言 Git 是一個開源的分布式版本控…

Java - Stream流式編程

Stream流式操作 Stream流式操作&#xff0c;就是學習java.util.stream包下的API&#xff0c;Stream不同于java的輸入輸出流&#xff0c;是實現對集合&#xff08;Collection&#xff09;的復雜操作&#xff0c;例如查找、替換、過濾和映射數據等&#xff0c;集合是一種靜態的數…

LeetCode547省份數量

題目描述 有 n 個城市&#xff0c;其中一些彼此相連&#xff0c;另一些沒有相連。如果城市 a 與城市 b 直接相連&#xff0c;且城市 b 與城市 c 直接相連&#xff0c;那么城市 a 與城市 c 間接相連。省份 是一組直接或間接相連的城市&#xff0c;組內不含其他沒有相連的城市。給…

第十一章 文件及IO操作

第十一章 文件及IO操作 文件的概述及基本操作步驟 文件&#xff1a; 存儲在計算機的存儲設備中的一組數據序列就是文件不同類型的文件通過后綴名進行區分 文本文件&#xff1a;由于編碼格式的不同&#xff0c;所占磁盤空間的字節數不同(例如GBK編碼格式中一個中文字符占2字…

cesium繪制三角網可視化及mesh網格數據解析

可視化運行效果(水質污染擴散) 實現運行效果 術語 Mesh網格數據解析 Mesh&#xff08;網格&#xff09;在不同領域有不同的應用和定義。在計算機網絡中&#xff0c;Mesh網絡指的是一種無中心的網狀結構&#xff0c;每個節點都與其他節點相連。而在3D計算機圖形學中&#…

云原生Kubernetes: K8S 1.26版本 部署KubeSphere

目錄 一、實驗 1.環境 2.K8S 1.26版本部署HELM 3.K8S 1.26版本 部署KubeSphere 4.安裝KubeSphere DevOps 二、問題 1.如何安裝Zadig 2.擴展插件Zadig安裝失敗 3.calico 如何實現不同node通信 4.如何清除docker占用的磁盤空間 5.如何強制刪除資源 6.namespace刪除不…

CGAL 點云生成高程模型數據(DSM)

點云生成高程模型 一、什么是DSM?二、C++代碼三、結果可視化一、什么是DSM? DSM(Digital Surface Model)是一種數字高程模型,通常用于描述地表地形的數字化表示。它是由一系列離散的高程數據點組成的三維地形模型,其中每個點都具有其相應的高程值。 ??DSM主要用于獲取和…