版权声明:本文为博主原创文章,转载请注明作者和链接。更多请继续关注 KunMinX

前言

前不久刚结束对 20 模块项目的第 3 轮重构,一路见证了 MVC、MVP、Clean 的优缺点并形成自己的体会。

近期在总结工作经验的同时,开始写博客。顺便开源了我设计的 ViaBus 架构。

项目地址:Github : KunMinX / android-viabus-architecture

⭐ 欢迎 star 和 fork ~

项目常用架构比对

以下,先对常见的 MVC、MVP、Clean、AAC 架构做个比对。

首先,一张表格展示各架构的类冗余情况:

需求是,写三个页面,ListFragment、DetailFragment、PreviewFragment,每个页面至少用到 3个 Note 业务、3个 User 业务。问:上述架构分别需编写多少类?

架构 涉及类 类总数
MVC Fragment:3个,Controller:3个,Model:2个 8个
MVP Fragment:3个,Presenter:3个,Model:3个,Contract:1个 10个
Clean Fragment:3个,ViewModel:3个,Usecase:18个,Model:3个 27个
AAC Fragment:3个,ViewModel:3个,Model:3个 9个

MVC 架构的缺陷

  • View、Controller、Model 相互依赖,造成代码耦合。
  • 难以分工,难以将 View、Controller、Model 分给不同的人写。
  • 难以维护,没有中间件接口做缓冲,难以替换底层的实现。
public class NoteListFragment extends BaseFragment {...public void refreshList() {new Thread(new Runnable() {@Overridepublic void run() {//view 中直接依赖 model。那么 view 须等 model 编写好才能开工。mNoteList = mDataManager.getNoteList();mHandler.sendMessage(REFRESH_LIST, mNoteList);}}).start();}private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg) {case REFRESH_LIST:mAdapter.setList(mNoteList);mAdapter.notifyDataSetChanged();break;default:}}};...
}

MVP 架构的特点与局限

  • MVP 架构的特点是 面向接口编程。在 View、Presenter、Model 之间分别用 中间件接口 做衔接,当有新的底层实现时,能够无缝替换。
  • 此外,MVP 的 View 和 Model 并不产生依赖,因此可以说是对 View 和 Model 做了代码解耦。
public class NoteListContract {interface INoteListView {void showDialog(String msg);void showTip(String tip);void refreshList(List<NoteBean> beans);}interface INoteListPresenter {void requestNotes(String type);void updateNotes(NoteBean... beans);void deleteNotes(NoteBean... beans);}interface INoteListModel {List<NoteBean> getNoteList();int updateNote(NoteBean bean);int deleteNote(NoteBean bean);}
}

但 MVP 架构有其局限性。按我的理解,MVP 设计的初衷是, “让天下没有难替换的 View 和 Model” 。该初衷背后所基于的假设是,“上层逻辑稳定,但底层实现更替频繁” 。在这个假设的引导下,使得三者中, 只有 Presenter 具备独立意志和决定权,掌管着 UI 逻辑和业务逻辑,而 View 和 Model 只是外接的工具

public class NoteListPresenter implements NoteListContract.INoteListPresenter {private NoteListContract.INoteListModel mDataManager;private NoteListContract.INoteListView mView;@Overridepublic void requestNotes(String type) {Observable.create(new ObservableOnSubscribe<List<NoteBean>>() {@Overridepublic void subscribe(ObservableEmitter<List<NoteBean>> e) throws Exception {List<NoteBean> noteBeans = mDataManager.getNoteList();e.onNext(noteBeans);}}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<List<NoteBean>>() {@Overridepublic void accept(List<NoteBean> beans) throws Exception {//presenter 直接干预了 UI 在拿到数据后做什么,使得逻辑上没有发生解耦。//正常来说,解耦意味着,presenter 的职能边界仅限返回结果数据,//由 UI 来依据响应码处理 UI 逻辑。mView.refreshList(beans);}});}...
}

然而,这样的假设多数时候并不实际。可视化需求是变化多端的,在牵涉到视觉交互时,必然涉及 UI 逻辑的修改,也就是说,View 和 Presenter 的相互牵连,使得 UI 的改动需要 View 和 Presenter 编写者配合着完成,增加沟通协作成本。

长久来看,二者都难以成长。Presenter 编写者容易被各种非本职工作拖累,View 的编写者不会尝试独立自主,例如通过多态等模式将 UI 封装成可适应性的组件,反正 ... 有 Presenter 来各种 if else 嘛。

Clean 架构的特点和不足

为解决 Presenter 职能边界不明确 的问题,在 Clean 架构中,业务逻辑的职能被转移到领域层,由 Usecase 专职管理。Presenter 则弱化为 ViewModel ,作为代理数据请求,和衔接数据回调的缓冲区。

Clean 架构的特点是 单向依赖、数据驱动编程View -> ViewModel -> Usecase -> Model

View 对 ViewModel 的单向依赖,是通过 databinding 特性实现的。ViewModel 只负责代理数据请求,在 Usecase 处理完业务返回结果数据时,结果数据被赋值给可观察的 databinding 数据,而 View 则依据数据的变化而变化。

public class NoteListViewModel {private ObservableList<NoteBean> mListObservable = new ObservableArrayList<>();private void requestNotes(String type) {if (null == mRequestNotesUsecase) {mRequestNotesUsecase = new ProveListInitUseCase();}mUseCaseHandler.execute(mRequestNotesUsecase, new RequestNotesUsecase.RequestValues(type),new UseCase.UseCaseCallback<RequestNotesUsecase.ResponseValue>() {@Overridepublic void onSuccess(RequestNotesUsecase.ResponseValue response) {//viewModel 的可观察数据发生变化后,databinding 会自动更新 UI 展示。mListObservable.clear();mListObservable.addAll(response.getNotes());}@Overridepublic void onError() {}});}...
}

但 Clean 架构也有不足:粒度太细 。一个 Usecase 受限于请求参数,因而只能处理一类请求。View 请求的数据包含几种类型,就至少需要准备几个 Usecase。Usecase 是依据当前 View 对数据的需求量身定制的,因此 Usecase 的复用率极低,项目会因而急剧的增加类和重复代码

public class RequestNotesUseCase extends UseCase<RequestNotesUseCase.RequestValues, RequestNotesUseCase.ResponseValue> {private DataManager mDataManager;@Overrideprotected void executeUseCase(final RequestValues values) {List<NoteBean> noteBeans = mDataManager.getNotes();...getUseCaseCallback().onSuccess(new RequestNotesUseCase.ResponseValue(noteBeans));}//每新建一个 usecase 类,都需要手动为其配置 请求参数列表 和 响应参数列表。public static final class RequestValues implements UseCase.RequestValues {private String type;public String getType() {return type;}public void setType(String type) {this.type = type;}}public static final class ResponseValue implements UseCase.ResponseValue {public List<NoteBean> mBeans;public ResponseValue(List<NoteBean> beans) {mBeans = beans;}}
}

AAC 架构的特点

AAC 也是数据驱动编程。只不过它不依赖于 MVVM 特性,而是直接在 View 中写个观察者回调,以接收结果数据并处理 UI 逻辑。

public class NoteListFragment extends BaseFragment {@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);viewModel.getNote().observe(this, new Observer<NoteBean>() {@Overridepublic void onChanged(@Nullable NoteBean bean) {//update UI}});}...
}

你完全可以将其理解为 B/S 架构:从 Web 前端向 Web 后端发送了数据请求,后端在处理完毕后响应结果数据给前端,前端再依据需求处理 UI 逻辑。等于说, AAC 将业务完全压到了 Model 层

ViaBus 架构的由来及特点

上一轮重构项目在用 Clean 架构,为此我决定跳过 AAC,基于对移动端数据交互的理解,编写“消息驱动编程”架构。

由于借助总线来代理数据的请求和响应,因此取名 ViaBus。

不同于以往的架构,ViaBus 明确界定了什么是 UI,什么是业务。

UI 的作用是视觉交互,为此 UI 的职责范围是请求数据和处理 UI 逻辑 。业务的作用是供应数据,因此 业务的职责范围是接收请求、处理数据、返回结果数据

UI 不需要知道数据是怎么来的、通过谁来的,它只需向 bus 发送一个请求,如果有业务注册了该类 “请求处理者”,那么自然有人来处理。业务也无需知道 UI 在拿到数据后会怎么用,它只需向 bus 回传结果,如果有 UI 注册了“观察响应者”,那么自然有人接收,并依据响应码行事。

这样,在静态 bus 的加持下,UI 和业务是完全解耦的,从根本上解决了相互牵连的问题。此外,不同于上述架构的每个 View 都要对应一个 Presenter 或 ViewModel,在 ViaBus 中,一个模块中的 UI 可以共享多个“业务处理者”实例,使 代码的复用率提升到100%

ViaBus 现已在 Github 开源,欢迎 Star & Fork ~

更多访问

Github : KunMinX / android-viabus-architecture
1分钟掌握 ViaBus 架构的使用
ViaBus - 年轻人的第一款 Android 架构

在抛弃 MVP-Clean 后,我自主设计并开源了 Viabus 架构相关推荐

  1. Eclipse Maven clean后错误: 找不到或无法加载主类com.xxx.ShopApplication

    如果com.xxx.ShopApplication原本可以启动但是在Maven中将模块clean后不能启动了,是因为系统找不到编译好的class文件,需要手动重新编译一下当前项目:Project-&g ...

  2. 用Kotlin语言开发玩安卓,基于基于Material Design+AndroidX + MVP + RxJava + Retrofit等优秀的开源框架开发,注释超详细,方便大家练手

    WanAndroid 一位练习时长两年半的安卓练习生根据鸿神提供的WanAndroid开放Api来制作的产品级App,基本实现了所有的功能,使用Kotlin语言,基于Material Design+A ...

  3. 创业失败后,我决定开源所有产品代码

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 整理 | 李冬梅(AI前线) 近年来,程序员创业的话题屡屡被提及,那么,程序员究竟适不适合创业?又该如 ...

  4. 抛弃 CSS Hacks 后的浏览器兼容方案

    一般情况下的浏览器兼容需要考虑 IE6/7/8 三种 IE 版本,当然在 IE9 开始逐步推向市场后,又会有更多的衍生版本.所以我目前只考虑 IE7~9 版本的兼容情况.涉及到的条件注释代码如下: & ...

  5. clean后class文件全部丢失_大数据专家,详解HadoopMapReduce处理海量小文件:压缩文件

    前言 在HDFS上存储文件,大量的小文件是非常消耗NameNode内存的,因为每个文件都会分配一个文件描述符,NameNode需要在启动的时候加载全部文件的描述信息,所以文件越多,对NameNode来 ...

  6. idea redis 插件_Redis客户端RDM收费后,还有哪些开源的替代品呢?

    作 者: Java面试那些事儿 原文链接:https://mp.weixin.qq.com/s/nyFOu1EEgWJxFOIAEFlUSw Redis的可视化客户端除了Redis Desktop M ...

  7. 北电的产品卖给Ciena后被拿去开源

    北电的城域以太网事业部MEN卖给Ciena后,DRAC产品被Ciena开源了. 叫Open DRAC: http://www.opendrac.org/ DRAC意思是动态资源分配控制器,它的基本理念 ...

  8. ​横扫六大权威榜单后,达摩院开源深度语言模型体系 AliceMind

    整理 | AI 科技大本营(ID:rgznai100) 自然语言处理(NLP)被誉为 AI 皇冠上的明珠,传统 NLP 模型制作复杂,耗时耗力,且用途单一,难以复用.预训练语言模型是 NLP 领域的研 ...

  9. WebMessenger完善后将作为一个开源项目

    先说说版权问题,现在WebMessenger客户端用的是微软的Web Msn Messenger的客户端,服务器端是我根据她的客户端模拟的一个,仅作学习用,不会传播和提供下载.因此目前的版本不方便开源 ...

最新文章

  1. Linux服务器程序编程的几个坎
  2. springcloud 与springboot的依赖关系以及版本的选择
  3. Remoting-1
  4. [css] 在sass中可以执行布尔运算吗?
  5. [转]VS 2003 常用快捷键
  6. 关于创建 LINQ to SQL 类时无法转换复数的问题(zhuan)
  7. 基于Spring Security的认证授权_认证原理_授权流程_Spring Security OAuth2.0认证授权---springcloud工作笔记126
  8. githua 账号合并_如何合并他人的分支 github
  9. 《3D数学基础》1.9 向量空间
  10. 苹果Mac 下 Parallels Desktop “无法连接到 Parallels 服务”的解决方法
  11. note同步不及时 one_续航800仅卖23万,比亚迪唐DM为啥却卖不过理想ONE?
  12. 数学建模真题训练——2003B题 CUMCM 露天矿生产的车辆安排(原创)
  13. centos pptp client 配置
  14. mysql ndb 安装_mysql NDB的安装配置使用示例
  15. 快速搭建多用户共享桌面云环境 云教室环境搭建
  16. Jieba分词的准确率提升:使用paddle模式进行分词(使用百度飞桨深度学习模型进行分词)
  17. 红帽 Linux 考试 要求
  18. 使用Kali上的Metasploit获取ssh登录到靶机权限
  19. 超硬核的Java工程师分享,什么是Java?为什么我要做Java,我是如何学习Java的?
  20. LuaBind --最强大的Lua C++ Bind

热门文章

  1. Postman批量跑接口
  2. java的多态性学习代码
  3. 变阻感器测量位移的计算机流程图,传感器工作流程图
  4. java图像分类源码_在Android设备上使用NCNN图像分类的demo
  5. 揭秘7大AI学习板块,这个星球推荐你拥有
  6. 【技术综述】有三说GANs(上)
  7. 全球及中国医疗监护仪市场容量需求与投资竞争力研究报告2022版
  8. java邮箱找回密码_Spring实现简单的邮箱找回密码功能
  9. git 常用操作命令(Common operation)
  10. Selenium+python --获取百度联想词