前言
对于Android架构,可能在很多人心里始终都是扑朔迷离的存在,似懂非懂、为了用而用、处处生吞活剥,这种状况应用的意义真的很无限。自己有多个我的项目重构的教训,恰好对设计畛域较为感兴趣,明天我将毫无保留的将本人对架构、设计的了解分享给大家。
本文不会具体去讲什么是MVC、MVP、MVVM
,但我形容的点应该都是这些模式的基石
,从实质上讲明确为什么
这样做,这样做的益处
是什么,有了这些底层思维
的反对再去看对应的架构模式
,置信会让你有一种面目一新的感觉。
常识储备:需把握Java面向对象、六大设计准则
,如果不了解也不妨,我尽量将用到的设计准则加以详细描述
目录
1. 模块化的意义何在?
- 1.1 基本概念以及底层思维
- 1.2 咱们要基于哪些个性去做模块化划分?
- 1.3 Android如何做分层解决?
- 1.4 Data Mapper或者是解药
- 1.5 无处安放的业务逻辑
2. 正当分层是给 数据驱动UI 做铺垫
- 2.1 什么是 管制反转?
- 2.2 什么是数据驱动UI?
- 2.3 为什么说数据驱动UI底层思维是管制反转?
- 2.4 为什么引入Diff?
3. 为什么我倡议应用 函数式编程
- 3.1 什么是 函数式编程?
- 3.2 Android视图开发能够借鉴函数式编程思维
1. 模块化的意义何在?
1.1 基本概念以及底层思维
所有的模块化都是为了满足繁多设计准则 (字面意思了解即可),一个函数或者一个类再或者一个模块,职责越繁多复用性就越强,同时可能间接升高耦合性
在软件工程的背景下,改变就会有出错的可能,不要说"我留神一点就不会出错"
这种话,因为人不是机器。咱们能做的就是尽可能让模块更加繁多,职责越繁多影响到外层模块的可能性就越小,这样出错的概率也就越低。
所以模块化核心思想即:繁多设计准则
1.2 咱们要基于哪些个性去做模块化划分?
做模块化解决的时候尽量基于两种个性进行性能个性
、业务个性
性能个性
网络、图片加载等等都可称之为性能个性。比方网络:咱们能够将网络框架的集成、封装等等写到同一个模块(module、package等)
当中,这样能够加强可读性(同一目录高深莫测)
、升高误操作
概率,不便于保护也更加平安。同时也可将模块托管至近程如maven库,可供多个我的项目应用,进一步晋升复用性
业务个性
业务个性字面意思了解即可,就是咱们经常编写的业务,须要以业务的个性进行模块划分
为什么说业务个性
优先级要高于性能个性
?
举个例子如下图:
置信很多人见过或者正在应用这种分包形式,在业务层把所有的Adapter
、Presenter
、Activity
等等都放在对应的包中,这种形式正当吗?先说答案不合理
,首先这曾经是在业务层,咱们做的所有事件其实都在为业务层服务,所以业务的优先级应该是最高的,咱们该当优先依据业务个性将对应的类放入到同一个包中。
功能模块
外围是性能,该当以性能
进行模块划分。业务模块
外围是业务,该当优先以业务
进行模块划分,其次再以性能
进行模块划分。
1.3 Android如何做分层解决?
前端开发其实就是做数据搬运,再展现到视图中。数据
与视图
是两个不同的概念,为了进步复用性以及可维护性,咱们该当依据繁多设计准则
咱们该当将二者进行分层解决,所以无论是MVC
、MVP
还是MVVM
最外围的点都是将数据
与视图
进行分层。
绊脚石:
通常来讲,咱们通过网络申请拿到数据结构都是后端定义的,这也就意味着视图层不得不间接应用后端定义的字段,一旦后端进行业务调整会迫使咱们前端从数据层-->视图层
都会进行对应的改变,如下伪代码所示:
//原始逻辑数据层Model{ title}UI层View{ textView = model.title}//后端调整后数据层Model{ title prefix}UI层View{ textView = model.prefix + model.title}
起初咱们的textView
显示的是model
中的title
,但后端调整后咱们须要在model
中加一个prefix
字段,同时textView
显示内容也要做一次字符串拼接。视图层因为数据层的改变而被动做了批改。既然做了分层咱们想要的必定是视图、数据互不烦扰,如何解决?往下看...
1.4 Data Mapper或者是解药
Data Mapper
是后端罕用的一个概念,个别状况下他们是不会间接应用数据库外面的字段,而是加一个Data Mapper(数据映射)
将数据库表转按需换成Java Bean
,这样做的益处也很显著,表构造甭管怎么折腾都不会影响到业务层代码。
对于前端我感觉能够适当引入Data Mapper
,将后端数据转换成本地模型
,本地模型只与设计图对应,将后端业务
与视图
齐全隔离。这也就解决了 1.3 面临的问题,具体形式如下:
数据层Model{ title prefix}本地模型(与设计图一一对应)LocalModel{ //将后端模型转换为本地模型 title = model.prefix + model.title}UI层View{ textView = localModel.title}
LocalModel
相当于一个中间层,通过适配器模式
将数据层与视图层做隔离。
前端引入Data Mapper
后能够脱离后端进行开发,只有需要明确就能够做视图
层的开发,齐全不须要放心后端返回什么构造
、字段
。并且这种做法是一劳永逸的,比方后端须要对某些字段做调整,咱们能够不暇考虑直奔数据层
,波及到的调整100%不会影响到视图层
留神点:
当下有一部分公司为了将前后端拆散更彻底,由前端开发人员提供Java Bean(相当于LocalModel)
的构造,益处也很显著,更多的业务内聚到后端,很大水平晋升了业务的灵活性,毕竟App发一次版老本还是比拟大的。面对这种状况咱们其实没必要再编写Data Mapper
。所以任何架构设计都要结合实际状况,适宜本人的才是最好的。
1.5 无处安放的业务逻辑
对于业务逻辑
其实是一个很抽象的概念,甚至能够将任意一行代码称之为业务逻辑
,如此宽泛的概念咱们该如何去了解?我先大抵将它分为两个方面:
- 界面交互逻辑:视图层的交互逻辑,比方手势管制、吸顶悬浮等等都是依据业务须要实现的,所以严格来说这部分也属于业务逻辑。但这部分
业务逻辑
个别在视图层实现。- 数据逻辑:这部分是大家常说的业务逻辑,属于强业务逻辑,比方依据不同用户类型获取不同数据、展现不同界面,加上Data Mapper一系列操作其实就是给后端兜底,帮他们补全残余逻辑而已。为了不便大家了解下文我将
数据逻辑
统称为业务逻辑
。
后面咱们说到,Android开发应该具备数据层
跟视图层
,那业务逻辑放在哪一层比拟适合呢?比方MVVM
模式下大家都说将业务逻辑
放到ViewModel
解决,这么说也没有太大的问题,但如果一个界面足够简单那对应的ViewModel
代码可能会有成千盈百行,看起来会很臃肿可读性也十分差。最重要的一点这些业务很难编写单元测试用例
。
对于业务逻辑我倡议独自写一个use case
解决。
use case
通常放在ViewModel/Presenter
与数据层
之间,业务逻辑以及Data Mapper
都应该放在use case
中,每一个行为对应一个use case
。这样就解决了ViewModel/Presenter
臃肿的问题,同时更不便编写测试用例。
留神点:
好的设计都是特定场景解决特定问题,适度设计不仅解决不了任何问题反而会减少开发成本。以我目前教训来看Android开发至多一半的场景都很简略:申请-->拿数据-->渲染视图
最多再加个Data Mapper
,流程很繁多并且前期改变的可能也不太大,这种状况就没必要写一个use case,Data Mapper
扔到数据层即可。
2. 正当分层是给 数据驱动UI 做铺垫
先说论断:数据驱动UI的实质是管制反转
2.1 什么是 管制反转?
管制
即对程序流程的管制,个别由咱们开发者承当,此过程为管制
。但开发者是人所以不可避免呈现谬误,此时能够将角色做一个反转
由成熟的框架负责整个流程,程序员只须要在框架预留的扩大点上,增加跟本人的业务代码,就能够利用框架来驱动整个程序流程的执行,此过程为反转
。
管制反转
概念和设计准则中的依赖倒置
很类似,只是少了一个依赖形象
。
打个比方:
现有一个HTTP申请
的需要,如果想本人保护HTTT链接
、本人治理TCP Socket
、本人解决HTTP缓存
.....就是整个HTTP协定
全副本人封装,先不说这个工程能不能靠集体实现,就算实现也是漏洞百出,此时能够换个思路:通过OkHttp
去实现,OkHttp
是一个成熟的框架用它基本上不会出错。集体封装HTTP协定
到应用OkHttp框架
,这个过程在管制
HTTP的角色上产生了一个反转
,集体--->成熟的框架OkHttp
即管制反转,益处也很显著,框架出错的概率远低于集体。
2.2 什么是数据驱动UI?
艰深一点说就是当数据扭转时对应的UI也要跟着变,反过来说当须要扭转UI只须要扭转对应的数据即可。当初比拟风行的UI框架如Flutter
、Compose
、Vue
其本质都是基于函数式编程实现数据驱动UI,它们独特的目标都是为了解决数据,UI一致性问题。
在以后的Android中能够应用DataBinding
实现同样的成果,以Jetpack MVVM
为例:ViewModel
从Repository
拿到数据暂存到ViewModel
对应的ObservableFiled
即可实现数据驱动UI,但前提是从Repository
拿到的数据能够间接用,如果在Activity
或者Adapter
做数据二次解决再notify UI
,曾经违反数据驱动UI核心思想。所以想实现数据驱动UI必须要有正当的分层(UI层拿到的数据无需解决,能够间接用)
,Data Mapper
恰好解决这一问题,同时也可躲避大量编写BindAdapter
的现状。
DataBinding
并非函数式编程,它只是通过AbstractProcessor
生成中间代码,将数据映射到XML中
2.3 为什么说数据驱动UI底层思维是管制反转?
以后Android生态能实现数据绑定UI的框架只有两个:DataBinding、Compose(暂不探讨)
在引入DataBinding之前渲染一条数据通常须要两步,如下:
var title = "iOS"fun setTitle(){ //第一步更改数据源 title = "Android" //第二个更改UI textView = title}
共须要两步更改数据源、更改UI,数据源
跟UI
有一个遗记批改便会呈现BUG,千万不要说:“两个我都不会遗记批改
”,当面临简单的逻辑以及十几个甚至几十个的数据源很难保障不出错。这种问题能够通过DataBinding
解决,只需更改对应的ObservableFiled
UI便会同步批改,管制
UI状态也从集体反转
到的DataBinding
,集体忽略的事件DataBinding
可不会。
所以说数据驱动UI底层思维是管制反转
2.4 为什么引入Diff?
引入diff
之前:
RecyclerView
想要实现动静删除、增加、更新须要别离手动更新数据和UI,这样在两头插了一道
并且别离更新数据和UI曾经违反了后面所说的数据驱动UI
,而咱们想要的是不论删除、增加或者更新只有一个入口,只有扭转数据源就会驱动UI做更新,想要满足这一准则只能扭转数据源后对RecyclerView
做全副刷新,但这样会造成性能问题,简单的界面会感到显著的卡顿。
引入diff
之后:
Diff
算法通过对oldItem
和newItem
做差异化比对,会自动更新扭转的item
,同时反对删除、增加的动画成果,这一个性解决了RecyclerView
须要实现数据驱动UI
的性能问题
3 为什么我倡议应用 函数式编程
3.1 什么是 函数式编程?
- 一个入口,一个进口。
- 不在函数链外部执行与运算自身无关的操作
- 不在函数链外部应用内部变量(实际上这一条很难恪守,能够适当冲破)
说的艰深点就是给定一个初始值,通过函数链的运行会失去一个目标值,运算的过程中内部没有插手的权限,同时不做与自身无关的操作,从根本上解决了不可预期谬误的产生。
举个例子:
//Kotlin代码listOf(10, 20).map { it + 1}.forEach { Log.i("list", "$it")}
下面这种链式编程就是规范的函数式编程,输出到输入之间开发者基本没有插手的机会(即Log.i(..)
之前开发者没有权限解决list),所以整个流程是100%
平安的,RxJava
、Flow
、链式高阶函数
都是规范的函数式编程,它们从标准
层面解决数据安全问题。所以我倡议在Kotlin
中 碰到数据处理尽量应用链式高阶函数(RxJava、Kotlin Flow亦然)
。
其实函数式编程的核心思想就是 门面模式 以及 迪米特法令
3.2 Android视图开发能够借鉴函数式编程思维
Android视图开发大都遵循如下流程:申请-->解决数据-->渲染UI,这一流程能够借鉴函数式编程,将申请作为入口,渲染做为进口,在这个流程中尽量不做与以后行为无关的事(这也要求ViewModel
,Repository
中的函数要合乎繁多准则)。这样说有点抽象,上面举个反例:
View{ //刷新 fun refresh(){ ViewModel.load(true) } //加载更多 fun loadMore(){ ViewModel.load(false) } } ViewModel{ //加载数据 load(isRefresh){ if (isRefresh){ //刷新 }else{ //加载更多 } } }
View
层有刷新、加载更多两种行为,load(isRefresh)
一个入口,两个进口。面临的问题很显著,批改刷新
或加载更多
都会对对方产生影响,违反开闭准则
中的闭(对批改敞开:行为没变不准批改源代码)
,导致存在不可预期的问题产生。能够借鉴函数式编程
思维对其进行改良,将ViewModel
的load
函数拆分成refresh
和loadMore
,这样刷新
和加载更多
两种行为、两个入口、两个进口互不干涉,通过函数的连接造成两条独立的业务链条。
函数式编程能够束缚咱们写出标准的代码,面对不能应用函数式编程的场景,咱们能够尝试自我束缚往函数式编程方向聚拢,大抵也能实现雷同的成果。
综上所述
- 正当的分层能够晋升复用性、升高模块间耦合性
- Data Mapper 能够让视图层脱离于后端进行开发
- 简单的业务逻辑应该写到use case中
- 数据驱动UI的实质是管制反转
- 通过函数式编程能够写出更加平安的代码
如果大家对Jetpack MVVM
感兴趣欢送留言
相干视频举荐:
【2021最新版】Android studio装置教程+Android(安卓)零基础教程视频(适宜Android 0根底,Android初学入门)含音视频_哔哩哔哩_bilibili
Android架构设计原理与实战——Jetpack联合MVP组合利用开发一个优良的APP!_哔哩哔哩_bilibili
Android进阶必学:jetpack架构组件—Navigation_哔哩哔哩_bilibili
Android进阶零碎学习——Jetpack先天优良的基因能够防止数据内存透露_哔哩哔哩_bilibili
本文转自 https://juejin.cn/post/6942464122273398820,如有侵权,请分割删除。