dcache-android框架中的设计模式详解

2024-05-25 16:20

本文主要是介绍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。这样也遵循了最小知识原则。如果你对框架的缓存机制不感兴趣,你只需要自己实现访问者。然后框架给你所有的缓存数据,你自己处理就好了,不用再细读源码。

总结

设计模式只是为了设计出更好扩展的系统,并不是非得为了使用设计模式而使用设计模式,具体还要看业务,有没有这个使用必要。当然开源框架本来就是给别人用的,所以设计模式用得比较多。架构设计的精髓不在于硬套设计模式进行设计,而是你设计得足够多了以后,不去硬性使用设计模式,而设计模式无处不在。这样你的架构设计能力就达到了一个新的境界了,设计模式你已经能完全掌控了。它已经融入到了你的骨髓,不是吗?

这篇关于dcache-android框架中的设计模式详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1002008

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存