MVC,MVP设计模式
什么是MVP
MVP是模型(Model)、视图(View)、主持人(Presenter)的缩写,分别代表项目中3个不同的模块。
模型(Model):负责处理数据的加载或者存储,比如从网络或本地数据库获取数据等;
视图(View):负责界面数据的展示,与用户进行交互;
主持人(Presenter):相当于协调者,是模型与视图之间的桥梁,将模型与视图分离开来。
如下图所示,View与Model并不直接交互,而是使用Presenter作为View与Model之间的桥梁。其中Presenter中同时持有Viwe层以及Model层的Interface的引用,而View层持有Presenter层Interface的引用。当View层某个界面需要展示某些数据的时候,首先会调用Presenter层的某个接口,然后Presenter层会调用Model层请求数据,当Model层数据加载成功之后会调用Presenter层的回调方法通知Presenter层数据加载完毕,最后Presenter层再调用View层的接口将加载后的数据展示给用户。这就是MVP模式的整个核心过程。
这样分层的好处就是大大减少了Model与View层之间的耦合度。一方面可以使得View层和Model层单独开发与测试,互不依赖。另一方面Model层可以封装复用,可以极大的减少代码量。当然,MVP还有其他的一些优点,这里不再赘述。下面看下MVP模式在具体项目中的使用。
MVP模式在项目中的使用
model层描述和具体代码
提供我们想要展示在view层的数据和具体登陆业务逻辑处理的实现,
1 package com.nsu.edu.androidmvpdemo.login; 2 3 /** 4 * Created by Anthony on 2016/2/15. 5 * Class Note:模拟登陆的操作的接口,实现类为LoginModelImpl.相当于MVP模式中的Model层 6 */ 7 public interface LoginModel { 8 void login(String username, String password, OnLoginFinishedListener listener); 9 }
1 package com.nsu.edu.androidmvpdemo.login; 2 3 import android.os.Handler; 4 import android.text.TextUtils; 5 /** 6 * Created by Anthony on 2016/2/15. 7 * Class Note:延时模拟登陆(2s),如果名字或者密码为空则登陆失败,否则登陆成功 8 */ 9 public class LoginModelImpl implements LoginModel { 10 11 @Override 12 public void login(final String username, final String password, final OnLoginFinishedListener listener) { 13 14 new Handler().postDelayed(new Runnable() { 15 @Override public void run() { 16 boolean error = false; 17 if (TextUtils.isEmpty(username)){ 18 listener.onUsernameError();//model层里面回调listener 19 error = true; 20 } 21 if (TextUtils.isEmpty(password)){ 22 listener.onPasswordError(); 23 error = true; 24 } 25 if (!error){ 26 listener.onSuccess(); 27 } 28 } 29 }, 2000); 30 } 31 }
2.2 view层描述和具体代码
负责显示数据、提供友好界面跟用户交互就行。MVP下Activity和Fragment以及View的子类体现在了这一 层,Activity一般也就做加载UI视图、设置监听再交由Presenter处理的一些工作,所以也就需要持有相应Presenter的引用。本层所需要做的操作就是在每一次有相应交互的时候,调用presenter的相关方法就行。(比如说,button点击)
1 package com.nsu.edu.androidmvpdemo.login; 2 3 /** 4 * Created by Anthony on 2016/2/15. 5 * Class Note:登陆View的接口,实现类也就是登陆的activity 6 */ 7 public interface LoginView { 8 void showProgress(); 9 10 void hideProgress(); 11 12 void setUsernameError(); 13 14 void setPasswordError(); 15 16 void navigateToHome(); 17 }
1 package com.nsu.edu.androidmvpdemo.login; 2 3 import android.app.Activity; 4 import android.content.Intent; 5 import android.os.Bundle; 6 import android.view.View; 7 import android.widget.EditText; 8 import android.widget.ProgressBar; 9 import android.widget.Toast; 10 11 import com.nsu.edu.androidmvpdemo.R; 12 13 /** 14 * Created by Anthony on 2016/2/15. 15 * Class Note:MVP模式中View层对应一个activity,这里是登陆的activity 16 */ 17 public class LoginActivity extends Activity implements LoginView, View.OnClickListener { 18 19 private ProgressBar progressBar; 20 private EditText username; 21 private EditText password; 22 private LoginPresenter presenter; 23 24 @Override 25 protected void onCreate(Bundle savedInstanceState) { 26 super.onCreate(savedInstanceState); 27 setContentView(R.layout.activity_login); 28 29 progressBar = (ProgressBar) findViewById(R.id.progress); 30 username = (EditText) findViewById(R.id.username); 31 password = (EditText) findViewById(R.id.password); 32 findViewById(R.id.button).setOnClickListener(this); 33 34 presenter = new LoginPresenterImpl(this); 35 } 36 37 @Override 38 protected void onDestroy() { 39 presenter.onDestroy(); 40 super.onDestroy(); 41 } 42 43 @Override 44 public void showProgress() { 45 progressBar.setVisibility(View.VISIBLE); 46 } 47 48 @Override 49 public void hideProgress() { 50 progressBar.setVisibility(View.GONE); 51 } 52 53 @Override 54 public void setUsernameError() { 55 username.setError(getString(R.string.username_error)); 56 } 57 58 @Override 59 public void setPasswordError() { 60 password.setError(getString(R.string.password_error)); 61 } 62 63 @Override 64 public void navigateToHome() { 65 // TODO startActivity(new Intent(this, MainActivity.class)); 66 Toast.makeText(this,"login success",Toast.LENGTH_SHORT).show(); 67 // finish(); 68 } 69 70 @Override 71 public void onClick(View v) { 72 presenter.validateCredentials(username.getText().toString(), password.getText().toString()); 73 } 74 75 }
2.3 presenter层描述和具体代码
Presenter扮演着view和model的中间层的角色。获取model层的数据之后构建view层;也可以收到view层UI上的反馈命令后分发处理逻辑,交给model层做业务操作。它也可以决定View层的各种操作。
1 package com.nsu.edu.androidmvpdemo.login; 2 3 /** 4 * Created by Anthony on 2016/2/15. 5 * Class Note:登陆的Presenter 的接口,实现类为LoginPresenterImpl,完成登陆的验证,以及销毁当前view 6 */ 7 public interface LoginPresenter { 8 void validateCredentials(String username, String password); 9 10 void onDestroy(); 11 }
1 package com.nsu.edu.androidmvpdemo.login; 2 3 /** 4 * Created by Anthony on 2016/2/15. 5 * Class Note: 6 * 1 完成presenter的实现。这里面主要是Model层和View层的交互和操作。 7 * 2 presenter里面还有个OnLoginFinishedListener, 8 * 其在Presenter层实现,给Model层回调,更改View层的状态, 9 * 确保 Model层不直接操作View层。如果没有这一接口在LoginPresenterImpl实现的话, 10 * LoginPresenterImpl只 有View和Model的引用那么Model怎么把结果告诉View呢? 11 */ 12 public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener { 13 private LoginView loginView; 14 private LoginModel loginModel; 15 16 public LoginPresenterImpl(LoginView loginView) { 17 this.loginView = loginView; 18 this.loginModel = new LoginModelImpl(); 19 } 20 21 @Override 22 public void validateCredentials(String username, String password) { 23 if (loginView != null) { 24 loginView.showProgress(); 25 } 26 27 loginModel.login(username, password, this); 28 } 29 30 @Override 31 public void onDestroy() { 32 loginView = null; 33 } 34 35 @Override 36 public void onUsernameError() { 37 if (loginView != null) { 38 loginView.setUsernameError(); 39 loginView.hideProgress(); 40 } 41 } 42 43 @Override 44 public void onPasswordError() { 45 if (loginView != null) { 46 loginView.setPasswordError(); 47 loginView.hideProgress(); 48 } 49 } 50 51 @Override 52 public void onSuccess() { 53 if (loginView != null) { 54 loginView.navigateToHome(); 55 } 56 } 57 }
2.4 登陆的回调接口
1 package com.nsu.edu.androidmvpdemo.login; 2 3 /** 4 * Created by Anthony on 2016/2/15. 5 * Class Note:登陆事件监听 6 */ 7 public interface OnLoginFinishedListener { 8 9 void onUsernameError(); 10 11 void onPasswordError(); 12 13 void onSuccess(); 14 }
demo的代码流程:(请参考下面的类图)
1 Activity做了一些UI初始化的东西并需要实例化对应LoginPresenter的引用和实现 LoginView的接口,监听界面动作
2 登陆按钮按下后即接收到登陆的事件,在onClick里接收到即通过LoginPresenter的引用把它交给LoginPresenter处理。LoginPresenter接收到了登陆的逻辑就知道要登陆了
3 然后LoginPresenter显示进度条并且把逻辑交给我们的Model去处理,也就是这里面的LoginModel,(LoginModel的实现类LoginModelImpl),同时会把OnLoginFinishedListener也就是LoginPresenter自身传递给我们的Model(LoginModel)。
4 LoginModel处理完逻辑之后,结果通过OnLoginFinishedListener回调通知LoginPresenter
5 LoginPresenter再把结果返回给view层的Activity,最后activity显示结果
请参考这张类图:

(3)注意:
3.1 presenter里面还有个OnLoginFinishedListener,其在Presenter层实现,给Model层回调,更改View层的状态,确保 Model层不直接操作View层。
3.2 在一个好的架构中,model层可能只是一个领域层和业务逻辑层的入口,如果我们参考网上比较火的Uncle Bob clean architecture model层可能是一个实现业务用例的交互者,在后续的文章中应该会涉及到这方面的问题,目前能力有限。暂时讲解到这里
本项目github地址:
https://github.com/CameloeAnthony/AndroidMVPDemo
第二例子源码地址:https://github.com/liuling07/SimpleNews
什么是MVC
MVC即Model-View-Controller。M:逻辑模型,V:视图模型,C:控制器。
MVC模式下,系统框架的类库被划分为3种:模型(Model)、视图(View)、控制器(Controller)。模型对象负责建立数据结构和相应的行为操作处理。视图对象负责在屏幕上渲染出相应的图形信息展示给用户看。控制器对象负责截获用户的按键和屏幕触摸等事件,协调Model对象和View对象。
用户与视图交互,视图接收并反馈用户的动作;视图把用户的请求传给相应的控制器,由控制器决定调用哪个模型,然后由模型调用相应的业务逻辑对用户请求进行加工处理,如果需要返回数据,模型会把相应的数据返回给控制器,由控制器调用相应的视图,最终由视图格式化和渲染返回的数据,对于返回的数据完全可以增加用户体验效果展现给用户。
一个模型可以有多个视图,一个视图可以有多个控制器,一个控制器也可以有多个模型。
MVC模式结构如下:
图1-1 MVC模式组件类型的关系和功能
模型(Model):封装的是数据源和所有基于对这些数据的操作。在一个组件中,Model往往表示组件的状态和操作状态的方法。
视图(View):封装的是对数据源Model的一种显示。一个模型可以由多个视图,而一个视图理论上也可以同不同的模型关联起来。
控制器(Control):封装的是外界作用于模型的操作。通常,这些操作会转发到模型上,并调用模型中相应的一个或者多个方法。一般Controller在Model和View之间起到了沟通的作用,处理用户在View上的输入,并转发给Model。这样Model和View两者之间可以做到松散耦合,甚至可以彼此不知道对方,而由Controller连接起这两个部分。
MVC应用程序总是由这三个部分组成。Event(事件)导致Controller改变Model或View,或者同时改变两者。只要Controller改变了Model的数据或者属性,所有依赖的View都会自动更新。类似的,只要Controller改变了View,View会从潜在的Model中获取数据来刷新自己。MVC模式最早是smalltalk语言研究团提出的,应用于用户交互应用程序中。
在设计模式中,MVC实际上是一个比较高层的模式,它由多个更基本的设计模式组合而成,Model-View的关系实际上是Observer模式,模型的状态和试图的显示相互响应,而View-Controller则是由Strategy模式所描述的,View用一个特定的Controller的实例来实现一个特定的响应策略,更换不同的Controller,可以改变View对用户输入的响应。而其它的一些设计模式也很容易组合到这个体系中。比如,通过Composite模式,可以将多个View嵌套组合起来;通过FactoryMethod模式来指定View的Controller,等等。在GOF书的 Introduction中,有一小节是“Design Patterns in Smalltalk MVC”即介绍在MVC模式里用到的设计模式。它大概向我们传达了这样的信息:合成模式+策略模式+观察者模式约等于MVC模式(当然MVC模式要多一些 东西)。
使用MVC的好处,一方面,分离数据和其表示,使得添加或者删除一个用户视图变得很容易,甚至可以在程序执行时动态的进行。Model和View能够单独的开发,增加了程序了可维护性,可扩展性,并使测试变得更为容易。另一方面,将控制逻辑和表现界面分离,允许程序能够在运行时根据工作流、用户习惯或者模型状态来动态选择不同的用户界面。因此,MVC模式广泛用于Web程序、GUI程序的架构。
这里实现一个Java应用程序。当用户在图形化用户界面输入一个球体的半径时,程序将显示该球体的体积与表面积。我们首先利用基本MVC模式实现以上程序,然后利用不同数量的模型、视图、控制器结构来扩展该程序。
Model与View的交互使用Observer模式。Model类必须继承Observable类,View类必须实现接口Observer。正是由于实现了上述结构,当Model发生改变时(Controller改变Model的状态),Model就会自动刷新与之相关的View。Controller类主要负责新建Model与View,将view与Mode相关联,并处理触发模型值改变的事件。

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库