Android mvp模式、mvvm模式
MVC和MVP的区别
2007年08月08日 星期三 上午 09:23
MVC和MVP到底有什么区别呢?
从这幅图可以看到,我们可以看到在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。
在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,及View。
所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。
Visual Studio等快速开发工具,让我们很难把View和Controller分开,我们总是直接在View的事件响应函数里完成了Controller的代码。而在ASP.NET和XAML里,使用了一种叫做Code-Behind的技术,可以把View和Controller进行分离。这样,View就可以完全由UI设计工程师来完成,而Controller由程序员来完成,两者可以直接合成不需要像现在一样再由程序员做很多的工作。
把Controller和View混在一起,有什么问题?
1.难以测试。
必须手动点击,使用各种自动化的测试工具。
2.代码难以重用。
UI是很难重用,因为要求总是不同。所以,导致重复的代码四处都是,维护麻烦。
MVP是如何解决MVC的问题的?
在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在 Presenter里实现。而且,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以 保持Presenter的不变,即重用!
不仅如此,我们还可以编写测试用的View(比如显示一个进度条,并不是真正显示进度条,只要把进度值打印出来观察即可,或者直接在测试view里略过ui的各种显示变化,只测试业务逻辑),模拟用户的各种操作,从而实现对Presenter的测试--而不需要使用自动化的测试工具。
我们甚至可以在Model和View都没有完成时候,就可以通过编写Mock Object(即实现了Model和View的接口,但没有具体的内容的)来测试Presenter的逻辑。
在MVP里,应用程序的逻辑主要在Presenter来实现,其中的View是很薄的一层。 因此就有人提出了Presenter First的设计模式,就是根据User Story来首先设计和开发Presenter。在这个过程中,View是很简单的,能够把信息显示清楚就可以了。在后面,根据需要再随便更改View, 而对Presenter没有任何的影响了。
如果要实现的UI比较复杂,而且相关的显示逻辑还跟Model有关系,就可以在View和 Presenter之间放置一个Adapter。由这个 Adapter来访问Model和View,避免两者之间的关联。而同时,因为Adapter实现了View的接口,从而可以保证与Presenter之 间接口的不变。这样就可以保证View和Presenter之间接口的简洁,又不失去UI的灵活性。
在MVP模式里,View只应该有简单的Set/Get的方法,用户用户输入和设置界面显示的内容,除此就不应该有更多的内容,绝不容许直接直接访问Model--这就是与MVC很大的不同之处。
mvp实现样例:
代码很简单就用登录的简单逻辑
Mvp代码实现
首先看下结构
- 数据逻辑相当于M
- Activity(负责View的绘制以及与用户交互)相当于V
- View与Model间的交互则为P
从M开始
UserModel.Class
public class UserModel {
private String username;
private String password;
public UserModel(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int checkUserValidity(String username, String password) {
if (username == null || password == null ||
username.isEmpty() ||
password.isEmpty()) {
return -1;
}
return 0;
}
}
接下来是V
ILoginView.Class
public interface ILoginView {
void showProgress();
void hideProgress();
void setPasswordError();
String getUsername();
String getPassword();
void loginSuccess();
}
LoginActivity.Class
public class LoginActivity extends AppCompatActivity implements ILoginView,View.OnClickListener{
private EditText usernameEdit,passwrodEdit;
private Button loginButton;
ProgressDialog pd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvplogin);
pd = new ProgressDialog(this);
usernameEdit = (EditText) findViewById(R.id.et_username);
passwrodEdit = (EditText) findViewById(R.id.et_username);
loginButton = (Button) findViewById(R.id.bt_login);
loginButton.setOnClickListener(this);
}
@Override
public void showProgress() {
pd.show();
}
@Override
public void hideProgress() {
pd.cancel();
}
@Override
public void setPasswordError() {
passwrodEdit.setError("passwrod error");
}
@Override
public String getUsername() {
return usernameEdit.getText().toString();
}
@Override
public String getPassword() {
return passwrodEdit.getText().toString();
}
@Override
public void loginSuccess() {
Toast.makeText(this, "login success", Toast.LENGTH_SHORT).show();
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.bt_login:
break;
}
}
最后就是P了
ILoginPresenter.Class
public interface ILoginPresenter {
void Login(String username, String password);
}
LoginPresenter.Class
public class LoginPersenter implements ILoginPresenter{
private ILoginView loginView;
private UserModel mUser;
public LoginPersenter(ILoginView loginView) {
this.loginView = loginView;
initUser();
}
private void initUser(){
mUser = new UserModel(loginView.getUsername(),loginView.getPassword());
}
@Override
public void Login(String username, String password) {
loginView.showProgress();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
loginView.hideProgress();
int code = mUser.checkUserValidity(loginView.getUsername(), loginView.getPassword());
if (code == -1) {
loginView.setPasswordError();
} else if (code == 0) {
loginView.loginSuccess();
}
}
},2000);
}
}
最后在LoginActivity中补上P的调用
//初始化
loginPresenter = new LoginPersenter(this);
//Click方法中的调用
loginPresenter.Login(usernameEdit.getText().toString(),passwrodEdit.getText().toString());
到这里MVP模式的代码就已经实现了
Mvvm代码实现
- Model:数据模型层。包含业务逻辑和校验逻辑
- View:屏幕上显示的UI界面(layout、views)
- ViewModel:View和Model之间的链接桥梁,处理视图逻辑。
从M开始
UserModel.Class
public class UserModel extends BaseObservable{
private String username;
private String password;
public UserModel(String username, String password) {
this.username = username;
this.password = password;
}
@Bindable
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
notifyPropertyChanged(BR.username);
}
@Bindable
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
notifyPropertyChanged(BR.password);
}
public int checkUserValidity() {
if (username == null || password == null ||
username.isEmpty() ||
password.isEmpty()) {
return -1;
}
return 0;
}
}
接下来是V
activity_mvvmlogin.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.netease.mvpormvvmdemo.mvvm.UserModel"/>
</data>
<LinearLayout
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/bt_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Login"
/>
</LinearLayout>
</layout>
最后是VM了
LoginActivity.Class
public class LoginActivity extends AppCompatActivity{
ActivityMvvmloginBinding binding;
ProgressDialog pd;
UserModel userModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvmlogin);
pd = new ProgressDialog(this);
binding.btLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
userModel = new UserModel(binding.etUsername.getText().toString(),binding.etPassword.getText().toString());
binding.setUser(userModel);
doLoign();
}
});
}
private void doLoign(){
pd.show();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
pd.cancel();
int code = userModel.checkUserValidity();
if (code == -1) {
binding.etPassword.setError("passwrod error");
} else if (code == 0) {
Toast.makeText(getBaseContext(), "login success", Toast.LENGTH_SHORT).show();
}
}
},2000);
}
}
到这里MVVM的代码也实现了
是不是看到这里觉得这不是很坑爹嘛?MVVM的写法和以前MVC的写法基本一样呀?这样还有什么意义?
确实一样,这是为什么呢?其实之前介绍的时候也有提到过Android中的Datebingding只能单向绑定,只能从ViewModel绑定到View中,所以呢View中数据的变化我们在ViewModel中并不能拿到,所以写法和MVC没有什么区别
但是我们可以从ViewModel绑定到View中,这里其实就有很大的变化了
那我们修改一下登陆逻辑,登录后清除界面元素信息,显示成功页或者失败页
MVP架构的代码就不书写了,和之前的是一样结构的,主要是来看下MVVM从ViewModel绑定到View中
首先修改下Model
public class UserModel extends BaseObservable{
private String username;
private String password;
private int status = 1;
@Bindable
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
notifyPropertyChanged(BR.username);
}
@Bindable
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
notifyPropertyChanged(BR.password);
}
@Bindable
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
notifyPropertyChanged(BR.status);
}
public void checkUserValidity() {
if (username == null || password == null ||
username.isEmpty() ||
password.isEmpty()) {
setStatus(-1);
}else {
setStatus(0);
}
}
}
这里主要是增加了一个新的属性为status,这个属性主要是用作判断当前界面的状态
接下来看看view的修改
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View"/>
<variable
name="user"
type="com.netease.mvpormvvmdemo.mvvm.UserModel"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="@{user.status == 1 ? View.VISIBLE : View.GONE}"
>
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/bt_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Login"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="@{user.status == 0 ? View.VISIBLE : View.GONE}"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.username}"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.password}"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="@{user.status == -1 ? View.VISIBLE : View.GONE}"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="error"
/>
</LinearLayout>
</RelativeLayout>
</layout>
view内主要是修改为三个布局,根据status的状态进行变化
最后看看viewmodel的变化,viewmodel其实没有怎么变化,因为model里面小的修改了下
private void doLoign(){
pd.show();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
pd.cancel();
userModel.checkUserValidity();
}
},2000);
}
这里的代码只有这些
当我们输入后点击login后,model会对输入状态取检测,从而改变model里面的status属性,而通过ViewModel,view会及时拿到最新的状态,从而通过判断去做view层的变化。这就是MVVM的绑定机制了
看下运行截图
Android mvp模式、mvvm模式的更多相关文章
- 浅谈MVC、MVP、MVVM模式
mvc的模式已经深入人心,想必大家都很熟悉,但是未必都能遵守mvc模式.我们的一个mvc项目比较简单,主要是数据库的查询.一个DBHelp类,封装了数据库的操作,然后Controller中进行中各种查 ...
- 转:界面之下:还原真实的 MVC、MVP、MVVM 模式
前言 做客户端开发.前端开发对MVC.MVP.MVVM这些名词不了解也应该大致听过,都是为了解决图形界面应用程序复杂性管理问题而产生的应用架构模式.网上很多文章关于这方面的讨论比较杂乱,各种MV*模式 ...
- MVC、MVP、MVVM 模式对比
MVC.MVP和MVVM这些开发模式为了分离视图(View)和模型(Model)而提出来的,直白说就是为了前后端分离. 1. MVC(Model View Controller)模式 MVC是比较直观 ...
- 设计模式之架构型MVC,MVP,MVVM模式
一.MVCMVC,Model View Controller,是软件架构中最常见的一种设计模式,简单来说就是通过Controller的控制去操作Model层的数据,并且返回给view层展示.View跟 ...
- MVC、MVP、MVVM模式
MVC,MVP和MVVM都是常见的软件架构设计模式(Architectural Pattern),它通过分离关注点来改进代码的组织方式.不同于设计模式(Design Pattern),只是为了解决一类 ...
- MVC,MVP 和 MVVM 模式如何选择?
转摘:http://www.linuxidc.com/Linux/2015-10/124622.htm 前言 做客户端开发.前端开发对MVC.MVP.MVVM这些名词不了解也应该大致听过,都是为了解决 ...
- MVC、MVP、MVVM 模式
一.前言 做客户端开发.前端开发对MVC.MVP.MVVM这些名词不了解也应该大致听过,都是为了解决图形界面应用程序复杂性管理问题而产生的应用架构模式.网上很多文章关于这方面的讨论比较杂乱,各种MV* ...
- MVC、MVP、MVVM模式的概念与区别
1. MVC框架 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示 ...
- 前端开发中的 MVC、MVP、MVVM 模式
MVC,MVP和MVVM都是常见的软件架构设计模式(Architectural Pattern),它通过分离关注点来改进代码的组织方式.不同于设计模式(Design Pattern),只是为了解决一类 ...
- angular 实战系列 之 mvvm模式
什么是MVVM模式 mvvm模式是mvc模式的一种变体,其中第一个m表示model,可以认为是数据对象的抽象,v代表view,vm代表view model ,是对view中的数据抽象(注1).mvvm ...
随机推荐
- [转] 多线程 《深入浅出 Java Concurrency》目录
http://ifeve.com/java-concurrency-thread-directory/ synchronized使用的内置锁和ReentrantLock这种显式锁在java6以后性能没 ...
- javascript 异常处理和事件处理
异常捕获 1.异常:当javascript引擎执行JS代码时,发生了错误,导致程序停止运行. 2.异常抛出:当异常产生,并且将这个异常生成一个错误信息 3.异常捕获: try{发生异常的代码块:}ca ...
- 动画--过渡延迟时间 transition-delay
transition-delay属性和transition-duration属性极其类似,不同的是transition-duration是用来设置过渡动画的持续时间,而transition-delay ...
- 夺命雷公狗---DEDECMS----24dedecms让网站头部分离
我们这里来做一个让网站头部分离的工作,我们先看下index.htm的模版, 这里很明显就是用了一个div包围着而已,那么我们在模版目录下创建一个head.htm,如下所示: 然后我们再将刚才div里面 ...
- android环境搭建——工欲善其事,必先利其器 2
前两天鼓捣android, 搭建环境,不想麻烦就用了 adt-bundle-windows-x86-20140702. rar , 起个模拟器哇塞,太爽了. 出去转一圈唠会回来正好启动成功!有个网友 ...
- SqlServer:此数据库处于单用户模式,导致数据库无法删除的处理
此数据库处理单用户模式,尚在连接当中,无法删除(既使将SQLServer停止后再启动也是如此) USE [master] GO /****** Object: StoredProcedure [dbo ...
- Javascript 类与静态类的实现-js面向对象
在Javascript里,对面向对象并没有一个直接的实现,对于代码方面也是非常的灵活. 今天所要说的就是,如何在Javascript里写类与静态类,这是本人一惯用的方法,你也可以有更为方便的,也可以发 ...
- 查看innodb表空间
使用脚本innodb_space,关于innodb的页管理方式可以参考Jeremy Cole的innodb的页管理方式, innodb_space -f test/t.ibd space-page-t ...
- Linux内核配置机制(make menuconfig 、Kconfig、Makefile)讲解【转】
本文转载自:http://www.codexiu.cn/linux/blog/34801/ 前面我们介绍模块编程的时候介绍了驱动进入内核有两种方式:模块和直接编译进内核,并介绍了模块的一种编译方式—— ...
- Web API 和 WCF 的比较
现在有很多可用的技术允许你创建被不同客户端所消费的服务,这些客户端可能是Web应用程序.Windows应用程序和移动应用等.服务可以支持http协议或者其他协议.接下来的讨论仅限于ASP.NET We ...