声明转载于作者:KunMinX
原文链接:https://www.jianshu.com/p/9ef...

前言

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

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

项目地址:
https://github.com/KunMinX/an...

项目常用架构比对

以下,对常见的 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%

APP瘦身这一篇就够了

一招教你打造一个滑动置顶的视觉特效

Android组件化demo实现以及遇坑分享

(Android)面试题级答案(精选版)

欢迎关注我微信公众号:终端研发部 ,如果您有什么问题可以一块学习和交流

Android:四大架构的优缺点,你真的了解吗? 1相关推荐

  1. Android:四大架构的优缺点,你真的了解吗?

    前言 前不久刚结束对 20 模块项目的第 3 轮重构,一路见证 MVC.MVP.Clean 的优缺点并形成自己的体会. 近期在总结工作经验的同时,开始写博客.顺便开源了我设计的 ViaBus 架构. ...

  2. Android 四大组件 与 MVC 架构模式

    作为一个刚从JAVA转过来的Android程序员总会思考android MVC是什么样的? 首先,我们必须得说Android追寻着MVC架构,那就得先说一下MVC是个啥东西! 总体而来说MVC不能说是 ...

  3. android底层 考试 华清,Android开发架构你真的了解吗—华清创客学院

    原标题:Android开发架构你真的了解吗-华清创客学院 华清创客学院讲师:我在网上翻过很多关于架构的文章,android也好,iOS也好,谈的更多的都是对工程结构的划分,涉及架构的部分非常少. 很多 ...

  4. Android四大组件之bindService源码实现详解

        Android四大组件之bindService源码实现详解 Android四大组件源码实现详解系列博客目录: Android应用进程创建流程大揭秘 Android四大组件之bindServic ...

  5. Android 开发架构-MVC MVP MVVM详解

    何为架构 架构,即程序的逻辑组织结构,是指导开发过程中划分程序逻辑模块的关键,好的架构要使程序达到高内聚低耦合的设计目标.例如一个人,身体的骨骼即为身体的架构,有了基本骨架之后,才可以决定在头颅里开发 ...

  6. Android零基础入门第2节:Android 系统架构和应用组件那些事

    继上一期浅谈了Android的前世今生,这一期一起来大致回顾一下Android 系统架构和应用组件. 一.Android 系统架构 Android系统的底层建立在Linux系统之上,该平台由操作系统. ...

  7. Android四大基本组件介绍与生命周期

    Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器. 一:了解四大基本组件 Activity ...

  8. Android四大基本组件和生命周期的介绍

    Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器. 一:了解四大基本组件 Activity ...

  9. android mvvm livedata_再谈Android应用架构——Jetpack VS 生命周期

    前面我们对MVC.MVP.MVVM进行了详尽的分析,但还有一个问题悬而未决,那就是生命周期.在Android平台上生命周期具有十分重要的意义,因此这也是架构必须考虑的因素之一.生命周期处理不好很容易发 ...

最新文章

  1. 用敏捷开发工具leangoo管理需求看板示例
  2. java第五章 多线程_java多线程编程核心技术——第五章总结
  3. Pytorch实战1:线性回归(Linear Regresion)
  4. 第七章:Java_集合
  5. mockjs(接口服务代理)
  6. 诺基亚宣布与博通合作开发5G芯片 包括定制处理器
  7. BZOJ 2019 [Usaco2009 Nov]找工作:spfa【最长路】【判正环】
  8. 【转】乐观锁和悲观锁的区别
  9. 最严格的身份证校验(Java版)
  10. 【路面分类】基于matlab灰度共生矩阵图形纹理检测+SVM路面状况分类【含Matlab源码 1519期】
  11. linux ssh x11,ssh服务器的x11 forwarding报错的解决
  12. 斜齿轮重合度计算公式_斜齿轮重合度计算
  13. 单元测试引入hsqldb探索
  14. 【控制系统的数学模型——传递函数】
  15. 使用oledb读写excel出现“操作必须使用一个可更新的查询”的解决办法
  16. vs2015——拖动选项卡导致软件崩溃重启
  17. 【菜鸟练习】用Java实现高尔顿瓶
  18. cousera上的华盛顿机器学习专项课程的案例学习学习经历分享
  19. typedef和define
  20. cdrx4自动排版步骤_教你怎样在CDR里怎样编号自动排版

热门文章

  1. 《HBase企业应用开发实战》—— 3.6 本章小结
  2. asp.net 运行原理
  3. 使用Log4j进行日志操作
  4. 微软 Exchange 服务器被滥用于内部邮件回复链攻击
  5. Windows 发布本地提权0day,可以系统权限执行任意代码
  6. 这个SSRF 漏洞很酷
  7. DBS-Function:f_GetPy
  8. swift项目调用OC库 和OC项目 在swift文件里面全局调用OC库
  9. 深入理解javascript原型和闭包 1
  10. Linux shell 脚本入门教程+实例