架构 MVC MVP MVVM 简介 MD
我的GitHub | 我的博客 | 我的微信 | 我的邮箱 |
---|---|---|---|
baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
MVC 架构
MVC各层的作用
- M层:Model,SQL、XML、JSON,数据模型。负责与数据处理相关的业务逻辑的处理,比如数据库读写操作,网络请求操作,复杂的算法,耗时的任务等。Model是一个应用系统的核心部分,代表了该系统实际要实现的所有功能。当M层完成数据处理后,会通知Controller更新View。
- V层:View,XML布局、自定义View,Java编写的View。负责在屏幕上渲染出相应的图形信息展示给用户看。
- C层:Controlle,Activity或者Fragmentr,控制器。负责接收如点击、触摸、电话呼入、网络改变等外部事件,并向Model层发送数据请求。同时负责接收Model层处理完数据后发的通知,并更新View。Controller是View和Model之间通信的桥梁。
Android中的实际情况
其实Android中只有MV
Android下MVC中的控制层是由Activity来承担的,Activity本来主要是作为初始化页面,展示数据的操作,但是因为XML视图功能太弱,所以Activity既要负责视图的显示又要加入控制逻辑,承担的功能过多。
比如对于登录页面,MVC的基本流程为:
用户与View交互,View接收并反馈用户的动作,View把用户的请求传给相应的控制器,由控制器决定调用哪个模型,然后由模型调用相应的业务逻辑对用户请求进行加工处理,如果需要返回数据,模型会把相应的数据返回给控制器,由控制器调用相应的视图,最终由视图渲染返回的数据。
在Android开发中,Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面,接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。
比如在Android中,对于登录页面,典型的交互过程是这样的:
用户点击登录按钮 → Activity中注册的监听器检测到点击事件 → Activity通过转动View中的ProgressBar来响应用户点击事件 → Activity通过View中的EditText获取用户输入的账户密码 → Activity将数据交由业务逻辑层(Model层)处理 → Model层处理完成后通过回调将数据返回给Activity → Activity更新UI反馈给用户
由上面的案例可以看出,其实这个View对应于布局文件能做的事情特别少,实际上关于该布局文件中的数据绑定、事件处理的代码都在Activity中,造成了Activity既像View又像Controller,这可能也就是为何:
Most of the modern Android applications just use View-Model
architecture,everything is connected with Activity.
演示案例
BaseModel
BaseModel顾名思义就是所有业务逻辑model的父类,这里的onDestroy()
方法用于跟activity或者fragment生命周期同步,在destroy做一些销毁操作
public interface BaseModel {
void onDestroy();
}
Callback
Callback是根据View或者Controller调用Model时回调的参数个数选择使用
public interface Callback1<T> {
void onCallBack(T t);
}
public interface Callback2<T, P> {
void onCallBack(T t, P p);
}
SampleModel
SampleModel是我们业务逻辑的具体实现
public class SampleModel implements BaseModel {
public void getUserInfo(String uid, Callback1<UserInfo> callback) {
UserInfo userInfo = new UserInfo();
//...从网络或数据库等获取数据
callback.onCallBack(userInfo);
}
public void getUserInfo2(String uid, Callback2<UserInfo, String> callback) {
UserInfo userInfo = new UserInfo();
//...从网络或数据库等获取数据
callback.onCallBack(userInfo, "其他数据");
}
@Override
public void onDestroy() {
}
public class UserInfo {
private int age;
private String name;
//...
}
}
SampleActivity
public class SampleActivity extends AppCompatActivity {
private SampleModel sampleModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
sampleModel = new SampleModel();
String uid = "123456";
findViewById(R.id.button).setOnClickListener(view -> getUserInfo(uid));
}
@Override
protected void onDestroy() {
super.onDestroy();
sampleModel.onDestroy();
}
//获取用户信息
private void getUserInfo(String uid) {
sampleModel.getUserInfo(uid, userInfo -> {
//...设置用户信息到view
});
}
}
案例总结
事件的流向
- button点击事件的触发:View → Controller
- 获取用户信息事件的触发:Controller → Model → Controller
- 绑定用户信息到View:Controller → View
总结
- 具有一定的分层,model彻底解耦,controller和view并没有解耦
- 层与层之间的交互尽量使用回调或者去使用消息机制去完成,尽量避免直接持有
- controller和view在android中无法做到彻底分离,但在代码逻辑层面一定要分清
- 业务逻辑被放置在model层,能够更好的复用和修改增加业务
MVP 架构
MVP的核心就是:让M和V完全解耦,通过Presenter统一调度管理
MVP的基本流程:Presenter从View中获取需要的参数,交给Model去处理,Model执行过程中的反馈以及结果告诉Presenter,Presenter再让View做对应的显示。
基本概念
MVP跟MVC很相像,根据MVC的发展来看,我们把MVP当成MVC来看也不为过,因为MVP也是三层,唯一的差别是Model和View之间不进行通讯
,都是通过Presenter完成。
前面介绍MVC的时候提到了算是致命缺点吧,在android中由于activity(god object)的存在,Controller和View很难做到完全解耦
。但在MVP中就可以很好的解决这个问题
特点
- 是MVC的演化版本
让Model和View完全解耦,由Presenter负责完成View与Model的交互
将Actvity视为View层
,减少了Activity的职责,将复杂的逻辑代码提取到了Presenter中
Presenter与View之间的交互完全是通过接口的
- 代码很清晰,不过增加了很多类;耦合度更低,更方便的进行测试;有助于协同开发,降低维护成本
MVP各层的作用
- Model:数据模型,和MVC中的Model一样
- View:对应UI界面(包括Activity、Fragment、以及所有视图),负责View的绘制以及与用户交互
- Presenter:调度者,负责完成View和Model间的交互
用MVP架构编写登录模块完整版
定义Presenter接口(可选)
分析这个模块需要哪些业务逻辑,或者说有哪些复杂的功能,以此定义Presenter接口。
对于登录模块,主要的就是登录功能。为了增加接口的复杂度,这里我又添加了一个退出前清理功能。
public interface Login_Presenter_I {
void login(String username, String password);//登录过程可能涉及到很工作,所以把它抽出来。此过程需要与View交互
void onFinishActivity();//退出前可能要做很多清理工作,所以也把它抽出来。此过程不需要与View交互
}
定义Model接口(可选)及MP回调接口(必选)
分析上述Presenter层中的功能在被Model层处理过程中,Model需要通知Presenter哪些内容,以此定义Model接口。
Model层在执行过程中,是通过接口通知Presenter执行过程中的状态以及执行完毕后的结果的,为了逻辑更清晰,建议此此接口定义为Model层接口的内部接口。
public interface Login_Model_I {
//参数 listener:Model层通过此接口通知Presenter执行过程中的状态以及执行完毕后的结果
void login(String username, String password, OnLoginListener listener);//执行过程中需要通知Presenter
void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache);//执行过程中不需要通知Presenter
//将Model层需要通知Presenter的内容,按照类别,定义在不同的接口中
interface OnLoginListener {
void onUsernameError();
void onPasswordError();
void onSuccess();
}
}
定义View接口(必选)
分析所有可能会操作UI的最基础逻辑,以此定义View接口。
Presenter层是通过接口来操作View层的。
public interface Login_Activity_I {
void showProgress();
void hideProgress();
void setUsernameError();
void setPasswordError();
void showToast(String msg, int duration);
}
定义Presenter的实现类
public class Login_Presenter_Impl implements Login_Presenter_I, Login_Model_I.OnLoginListener {
private Login_Activity_I loginActivityI; //拿到的是接口,整个Presenter中没有导入任何View和Activity
private Login_Model_I loginIModel; //拿到的是Model层的实现类
public Login_Presenter_Impl(Login_Activity_I loginActivityI) {
this.loginActivityI = loginActivityI;//View层的实现类,由Activity传过来
this.loginIModel = new Login_Model_Impl();//Model层的实现类,由Activity传过来或自己创建均可
}
@Override
public void login(String username, String password) {
if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI
loginIModel.login(username, password, this);//交给Model层处理
}
@Override
public void onFinishActivity() {
if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI
boolean clearSp = new Random().nextBoolean();
boolean deleteCache = new Random().nextBoolean();
loginIModel.clearBeforeFinishActivity(clearSp, deleteCache);//交给Model层处理
if (loginActivityI != null) loginActivityI.hideProgress();//通知View更新UI
}
@Override
public void onUsernameError() {
if (loginActivityI != null) {
loginActivityI.setUsernameError();
loginActivityI.hideProgress();
}
}
@Override
public void onPasswordError() {
if (loginActivityI != null) {
loginActivityI.setPasswordError();
loginActivityI.hideProgress();
}
}
@Override
public void onSuccess() {
if (loginActivityI != null) loginActivityI.showToast("登录成功", Toast.LENGTH_SHORT);
}
}
定义Model的实现类
Model层的实现类,只处理逻辑,完全不操作View,对逻辑处理的状态反馈给Presenter
public class Login_Model_Impl implements Login_Model_I {
@Override
public void login(String username, String password, OnLoginListener listener) {
if (TextUtils.isEmpty(username)) listener.onUsernameError();
else if (TextUtils.isEmpty(password)) listener.onPasswordError();
else listener.onSuccess();
}
@Override
public void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache) {
if (clearSp) Log.i("bqt", "【清理SP】");
if (deleteCache) Log.i("bqt", "【清理缓存】");
}
}
让Activity实现View接口
public class Login_Activity extends Activity implements Login_Activity_I, View.OnClickListener {
private ProgressBar progressBar;
private EditText username;
private EditText password;
private Login_Presenter_I presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
progressBar = (ProgressBar) findViewById(R.id.progress);
username = (EditText) findViewById(R.id.username);
password = (EditText) findViewById(R.id.password);
findViewById(R.id.button).setOnClickListener(this);
presenter = new Login_Presenter_Impl(this);//new一个Presenter的实现类,把自己传过去。实际上接收的只是LoginView接口的实例
}
@Override
public void finish() {
presenter.onFinishActivity();
super.finish();
}
@Override
public void showProgress() {
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideProgress() {
progressBar.setVisibility(View.GONE);
}
@Override
public void setUsernameError() {
username.setError(getString(R.string.username_error));
}
@Override
public void setPasswordError() {
password.setError(getString(R.string.password_error));
}
@Override
public void showToast(String msg, int duration) {
Toast.makeText(this, msg, duration).show();
}
@Override
public void onClick(View v) {
//点击登录时,View把数据传给presenter,presenter处理完数据后通知View处理事件
presenter.login(username.getText().toString(), password.getText().toString());
}
}
用MVP架构编写登录模块简洁版
View层接口
public interface LoginView {
void showProgress();
void hideProgress();
void setUsernameError();
void setPasswordError();
void navigateToHome();
}
Activity
public class LoginActivity extends AppCompatActivity implements LoginView {
private ProgressBar progressBar;
private EditText username;
private EditText password;
private LoginPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
progressBar = findViewById(R.id.progress);
username = findViewById(R.id.username);
password = findViewById(R.id.password);
findViewById(R.id.button).setOnClickListener(v -> login());
presenter = new LoginPresenter(this, new LoginModel());
}
@Override
protected void onDestroy() {
presenter.onDestroy();
super.onDestroy();
}
@Override
public void showProgress() {
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideProgress() {
progressBar.setVisibility(View.GONE);
}
@Override
public void setUsernameError() {
username.setError("用户名错误");
}
@Override
public void setPasswordError() {
password.setError("密码错误");
}
@Override
public void navigateToHome() {
startActivity(new Intent(this, MainActivity.class));
finish();
}
private void login() {
presenter.login(username.getText().toString(), password.getText().toString());
}
}
Presenter
public class LoginPresenter implements LoginModel.OnLoginFinishedListener {
private LoginView LoginView;
private LoginModel loginModel;
LoginPresenter(LoginView LoginView, LoginModel loginModel) {
this.LoginView = LoginView;
this.loginModel = loginModel;
}
public void login(String username, String password) {
if (LoginView != null) {
LoginView.showProgress();
}
loginModel.login(username, password, this);
}
public void onDestroy() {
LoginView = null;
}
@Override
public void onUsernameError() {
if (LoginView != null) {
LoginView.setUsernameError();
LoginView.hideProgress();
}
}
@Override
public void onPasswordError() {
if (LoginView != null) {
LoginView.setPasswordError();
LoginView.hideProgress();
}
}
@Override
public void onSuccess() {
if (LoginView != null) {
LoginView.navigateToHome();
}
}
}
Model
public class LoginModel {
interface OnLoginFinishedListener {
void onUsernameError();
void onPasswordError();
void onSuccess();
}
public void login(final String username, final String password, final OnLoginFinishedListener listener) {
new Handler().postDelayed(() -> {
if (TextUtils.isEmpty(username)) {
listener.onUsernameError();
return;
}
if (TextUtils.isEmpty(password)) {
listener.onPasswordError();
return;
}
listener.onSuccess();
}, 2000);
}
}
MVVM 架构
MVP中,随着业务逻辑的增加,UI的改变多的情况下,会有非常多的跟UI相关的case,这样就会造成View的接口会很庞大。而MVVM就解决了这个问题,通过双向绑定
的机制,实现数据和UI内容,只要想改其中一方,另一方都能够及时更新的一种设计理念,这样就省去了很多在View层中写很多case的情况,只需要改变数据就行。
先看下MVVM设计图:
这看起来跟MVP好像没啥差别,其实区别还是挺大的,在MVP中,View和presenter要相互持有,方便调用对方,而在MVP中,View和ViewModel通过Binding进行关联,他们之前的关联处理通过DataBinding
完成。
MVVM与DataBinding的关系用一句话表述就是,MVVM是一种思想,DataBinding是谷歌推出的方便实现MVVM的工具
。
在google推出DataBinding之前,因为xml layout功能较弱,想实现MVVM非常困难。而DataBinding的出现可以让我们很方便的实现MVVM。
看起来MVVM很好的解决了MVC和MVP的不足,但是由于数据和视图的双向绑定,导致出现问题时不太好定位来源
,有可能数据问题导致,也有可能业务逻辑中对视图属性的修改导致。如果项目中打算用MVVM的话可以考虑使用官方的架构组件ViewModel、LiveData、DataBinding
去实现MVVM。
关于ViewModel、LiveData、DataBindin这些类或框架的使用,因为涉及到的内容比较多,这里不详细介绍。
如何选择
前面在介绍MVC、MVP、MVVM时并没有去详细列出他们的优缺点,主要原因是:关于架构,设计,模块化等等,它们的优缺点没有绝对的,主要看实现者如何去做
比如在mvp中我们要实现根据业务逻辑和页面逻辑做很多Present和View的具体实现,如果这些case太多,会导致代码的可读性变差。但是通过引入contract契约类,会让业务逻辑变得清晰许多。因此不管是用哪种设计模式,只要运用得当,都可以达到想要的结果。
如果非要说怎么选的话,一般的建议如下:
- 如果
项目简单
,没什么复杂性,未来改动也不大的话,那就不要用设计模式或者架构方法,只需要将每个模块封装好,方便调用即可,不要为了使用设计模式或架构方法而使用。 - 对于偏向
展示型
的app,绝大多数业务逻辑都在后端,app主要功能就是展示数据,交互等,建议使用mvvm
。 - 对于工具类或者需要写很多
业务逻辑
app,使用mvp或者mvvm都可。 - 如果想通过一个项目去学习架构和设计模式,建议用
MVC
然后在此基础上慢慢挖掘改进。最后你可能发现,改进的最终结果可能就变成了mvp,mvvm。
2019-5-9
架构 MVC MVP MVVM 简介 MD的更多相关文章
- [转]MVVM架构~mvc,mvp,mvvm大话开篇
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负 责显示.作为一种新的模式,MVP与MVC有着一 ...
- MVVM架构~mvc,mvp,mvvm大话开篇
返回目录 百度百科的定义: MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负 责显示.作为一种新 ...
- Android App的设计架构:MVC,MVP,MVVM与架构经验谈
相关:http://www.cnblogs.com/wytiger/p/5996876.html 和MVC框架模式一样,Model模型处理数据代码不变在Android的App开发中,很多人经常会头疼于 ...
- Android App的设计架构:MVC,MVP,MVVM与架构AAAAA
1. 架构设计的目的1.1 通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合.1.2 这样做的好处是使得程序在开发的过程中,开发人员只需要专注于一点,提高程序开发的效率,并且更容易进行后续 ...
- 前端mvc mvp mvvm 架构介绍(vue重构项目一)
首先 我们为什么重构这个项目 1:我们现有的技术是前后台不分离,页面上采用esayUI+jq构成的单页面,每个所谓的单页面都是从后台胜场的唯一Id 与前端绑定,即使你找到了那个页面元素,也找不到所在的 ...
- MVC, MVP, MVVM比较以及区别(上)
MVC, MVP和MVVM都是用来解决界面呈现和逻辑代码分离而出现的模式.以前只是对它们有部分的了解,没有深入的研究过,对于一些里面的概念和区别也是一知半解.现在一边查资料,并结合自己的理解,来谈一下 ...
- android MVC && MVP && MVVM分析和对比
相关:http://www.cnblogs.com/wytiger/p/5305087.html 出处http://blog.csdn.net/self_study,对技术感兴趣的同鞋加群544645 ...
- MVC, MVP, MVVM比较以及区别
MVC, MVP和MVVM都是用来解决界面呈现和逻辑代码分离而出现的模式.以前只是对它们有部分的了解,没有深入的研究过,对于一些里面的概念和区别也是一知半解.现在一边查资料,并结合自己的理解,来谈一下 ...
- 浅谈Android架构之MVP,MVVM
概述 MVP(Model-View-Presenter)是传统MVC(Model-View-Controller)在Android开发上的一种变种.进化模式.主要用来隔离UI.UI逻辑和业务逻辑.数据 ...
随机推荐
- Android Studio 之 ViewModel
ViewModel 是 JetPack 类库中的一个功能,可以保存控件的状态 ,在整个Activity 生命周期中,状态不会失效 如屏幕翻转时,状态可保留,不会失效! 与 LiveData 配合使用! ...
- IDEA 调试图文教程,让 bug 无处藏身!
阅读本文大概需要 6.2 分钟. 来源:http://t.cn/EoPN7J2 Debug用来追踪代码的运行流程,通常在程序运行过程中出现异常,启用Debug模式可以分析定位异常发生的位置,以及在运行 ...
- mysql将多条结果拼接成一条结果
1,实际数据 SELECT resource_id, resource_type FROM res_resource_mount 2,拼接之后数据 SELECT c.resource_id, GROU ...
- [Beta]Scrum Meeting#1
github 本次会议项目由PM召开,时间为5月6日晚上10点30分 时长15分钟 任务表格 人员 昨日工作 下一步工作 木鬼 beta初步计划 撰写博客整理文档 swoip 前端改进计划 模块松耦合 ...
- webpack配置css浏览器前缀
webpack打包时,css自动添加浏览器前缀.我们需要用到一个Loader:postcss-loader,和一个插件:autoprefixer 安装 npm i postcss-loader aut ...
- Understanding Variational Autoencoders (VAEs)
Understanding Variational Autoencoders (VAEs) 2019-09-29 11:33:18 This blog is from: https://towards ...
- Declaration of Admin\Controller\GameController::delete() should be compatible with。。
NOTIC: [2048] Declaration of Admin\Controller\GameController::delete() should be compatible with A ...
- 【KakaJSON手册】08_其他用法
除了完成JSON和Model的转换之外,KakaJSON内部还有很多实用的功能,有些也开放为public接口了 遍历属性 struct Cat { var age: Int = 0 let name: ...
- 《Effective Java》第1章 创建和销毁对象
第1条 用静态工厂方法代替构造器 这个静态工厂,与设计模式中的静态工厂不同,这里的静态工厂方法,替换为“静态方法”比较好理解,主要就是建议编写静态方法来创建对象. 使用静态方法的好处: 1.静态方法有 ...
- SDN实验---Mininet实验(模拟多数据中心带宽实验)
补充:NameError: name 'buffer' is not defined >>> import sys >>> ,): ... buffer = mem ...