java mvc mvp_MVC,MVP设计模式

MVP模式是一种软件设计模式,由模型、视图、主持人三个部分构成。模型负责数据处理,视图负责界面展示,主持人作为两者间的桥梁。通过这种方式,MVP降低了视图与模型之间的耦合度,便于独立开发和测试。文章通过登录模块的例子详细介绍了MVP的模型、视图和主持人的职责,以及它们如何交互。此外,文章还对比了MVC模式,解释了两者的区别和应用场景。
摘要由CSDN通过智能技术生成

什么是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模式的整个核心过程。

884ee8c92351b1ed6399128d3d6f90b9.png

这样分层的好处就是大大减少了Model与View层之间的耦合度。一方面可以使得View层和Model层单独开发与测试,互不依赖。另一方面Model层可以封装复用,可以极大的减少代码量。当然,MVP还有其他的一些优点,这里不再赘述。下面看下MVP模式在具体项目中的使用。

MVP模式在项目中的使用

model层描述和具体代码

提供我们想要展示在view层的数据和具体登陆业务逻辑处理的实现,

1 packagecom.nsu.edu.androidmvpdemo.login;2

3 /**

4 * Created by Anthony on 2016/2/15.5 * Class Note:模拟登陆的操作的接口,实现类为LoginModelImpl.相当于MVP模式中的Model层6 */

7 public interfaceLoginModel {8 voidlogin(String username, String password, OnLoginFinishedListener listener);9 }

1 packagecom.nsu.edu.androidmvpdemo.login;2

3 importandroid.os.Handler;4 importandroid.text.TextUtils;5 /**

6 * Created by Anthony on 2016/2/15.7 * Class Note:延时模拟登陆(2s),如果名字或者密码为空则登陆失败,否则登陆成功8 */

9 public class LoginModelImpl implementsLoginModel {10

11 @Override12 public void login(final String username, final String password, finalOnLoginFinishedListener listener) {13

14 new Handler().postDelayed(newRunnable() {15 @Override public voidrun() {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 packagecom.nsu.edu.androidmvpdemo.login;2

3 /**

4 * Created by Anthony on 2016/2/15.5 * Class Note:登陆View的接口,实现类也就是登陆的activity6 */

7 public interfaceLoginView {8 voidshowProgress();9

10 voidhideProgress();11

12 voidsetUsernameError();13

14 voidsetPasswordError();15

16 voidnavigateToHome();17 }

1 packagecom.nsu.edu.androidmvpdemo.login;2

3 importandroid.app.Activity;4 importandroid.content.Intent;5 importandroid.os.Bundle;6 importandroid.view.View;7 importandroid.widget.EditText;8 importandroid.widget.ProgressBar;9 importandroid.widget.Toast;10

11 importcom.nsu.edu.androidmvpdemo.R;12

13 /**

14 * Created by Anthony on 2016/2/15.15 * Class Note:MVP模式中View层对应一个activity,这里是登陆的activity16 */

17 public class LoginActivity extends Activity implementsLoginView, View.OnClickListener {18

19 privateProgressBar progressBar;20 privateEditText username;21 privateEditText password;22 privateLoginPresenter presenter;23

24 @Override25 protected voidonCreate(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 @Override38 protected voidonDestroy() {39 presenter.onDestroy();40 super.onDestroy();41 }42

43 @Override44 public voidshowProgress() {45 progressBar.setVisibility(View.VISIBLE);46 }47

48 @Override49 public voidhideProgress() {50 progressBar.setVisibility(View.GONE);51 }52

53 @Override54 public voidsetUsernameError() {55 username.setError(getString(R.string.username_error));56 }57

58 @Override59 public voidsetPasswordError() {60 password.setError(getString(R.string.password_error));61 }62

63 @Override64 public voidnavigateToHome() {65 //TODO startActivity(new Intent(this, MainActivity.class));

66 Toast.makeText(this,"login success",Toast.LENGTH_SHORT).show();67 //finish();

68 }69

70 @Override71 public voidonClick(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 packagecom.nsu.edu.androidmvpdemo.login;2

3 /**

4 * Created by Anthony on 2016/2/15.5 * Class Note:登陆的Presenter 的接口,实现类为LoginPresenterImpl,完成登陆的验证,以及销毁当前view6 */

7 public interfaceLoginPresenter {8 voidvalidateCredentials(String username, String password);9

10 voidonDestroy();11 }

1 packagecom.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 implementsLoginPresenter, OnLoginFinishedListener {13 privateLoginView loginView;14 privateLoginModel loginModel;15

16 publicLoginPresenterImpl(LoginView loginView) {17 this.loginView =loginView;18 this.loginModel = newLoginModelImpl();19 }20

21 @Override22 public voidvalidateCredentials(String username, String password) {23 if (loginView != null) {24 loginView.showProgress();25 }26

27 loginModel.login(username, password, this);28 }29

30 @Override31 public voidonDestroy() {32 loginView = null;33 }34

35 @Override36 public voidonUsernameError() {37 if (loginView != null) {38 loginView.setUsernameError();39 loginView.hideProgress();40 }41 }42

43 @Override44 public voidonPasswordError() {45 if (loginView != null) {46 loginView.setPasswordError();47 loginView.hideProgress();48 }49 }50

51 @Override52 public voidonSuccess() {53 if (loginView != null) {54 loginView.navigateToHome();55 }56 }57 }

2.4 登陆的回调接口

1 packagecom.nsu.edu.androidmvpdemo.login;2

3 /**

4 * Created by Anthony on 2016/2/15.5 * Class Note:登陆事件监听6 */

7 public interfaceOnLoginFinishedListener {8

9 voidonUsernameError();10

11 voidonPasswordError();12

13 voidonSuccess();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显示结果

请参考这张类图:

68532ab6cfb7b4430450119bac75237a.png

(3)注意:

3.1 presenter里面还有个OnLoginFinishedListener,其在Presenter层实现,给Model层回调,更改View层的状态,确保 Model层不直接操作View层。

3.2 在一个好的架构中,model层可能只是一个领域层和业务逻辑层的入口,如果我们参考网上比较火的Uncle Bob clean architecture model层可能是一个实现业务用例的交互者,在后续的文章中应该会涉及到这方面的问题,目前能力有限。暂时讲解到这里

什么是MVC

MVC即Model-View-Controller。M:逻辑模型,V:视图模型,C:控制器。

MVC模式下,系统框架的类库被划分为3种:模型(Model)、视图(View)、控制器(Controller)。模型对象负责建立数据结构和相应的行为操作处理。视图对象负责在屏幕上渲染出相应的图形信息展示给用户看。控制器对象负责截获用户的按键和屏幕触摸等事件,协调Model对象和View对象。

用户与视图交互,视图接收并反馈用户的动作;视图把用户的请求传给相应的控制器,由控制器决定调用哪个模型,然后由模型调用相应的业务逻辑对用户请求进行加工处理,如果需要返回数据,模型会把相应的数据返回给控制器,由控制器调用相应的视图,最终由视图格式化和渲染返回的数据,对于返回的数据完全可以增加用户体验效果展现给用户。

一个模型可以有多个视图,一个视图可以有多个控制器,一个控制器也可以有多个模型。

MVC模式结构如下:

7de8eba53c9f59aa1f3bb133c7896a5b.png

图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相关联,并处理触发模型值改变的事件。

f94b8baf4b5e73afc42d2fceadb47e3c.png

1 importjava.util.Observable;2

3 //Sphere.java:Model类4 //必须继承Observable,在Observable类中,方法addObserver()将视图与模型相关联

5 class Sphere extendsObservable {6

7 private doublemyRadius;8

9 public void setRadius(doubler) {10 myRadius =r;11 this.setChanged(); //指示模型已经改变

12 this.notifyObservers(); //通知各个视图,从父继承的方法

13 }14 //......

15 }

1 importjava.util.Observable;2 importjava.util.Observer;3 importjavax.swing.JPanel;4

5 //TextView.java:View视图类6 //当模型Sphere类的状态发生改变时,与模型相关联的视图中的update()方法7 //就会自动被调用,从而实现视图的自动刷新

8 public class TextView extends JPanel implementsObserver {9

10 @Override11 public voidupdate(Observable o, Object arg) {12 Sphere balloon =(Sphere) o;13 radiusIn.setText("" +f3.format(balloon.getRadius()));14 volumeOut.setText("" +f3.format(balloon.volume()));15 surfAreaOut.setText("" +f3.format(balloon.surfaceArea()));16 }17 //......

18 }

1 importjava.awt.Container;2 importjava.awt.event.ActionEvent;3 importjavax.swing.JFrame;4 importjavax.swing.JTextField;5

6 //SphereWindow.java:Controller类7 //它主要新建Model与View,将view与Mode相关联,并处理事件

8 public class SphereWindow extendsJFrame {9

10 publicSphereWindow() {11 super("Spheres: volume and surface area");12 model = new Sphere(0, 0, 100); //新建Model

13 TextView view = new TextView(); //新建View

14 model.addObserver(view); //将View与Model相关联

15 view.update(model, null); //初始化视图,以后就会根据Model的变化自动刷新

16 view.addActionListener(this);17 Container c =getContentPane();18 c.add(view);19 }20

21 //处理事件:改变Model的状态

22 public voidactionPerformed(ActionEvent e) {23 JTextField t =(JTextField) e.getSource();24 double r =Double.parseDouble(t.getText());25 model.setRadius(r);26 }27 //......

28 }

这种MVC模式的程序具有极其良好的可扩展性。它可以轻松实现一个模型的多个视图;可以采用多个控制器;可以实现当模型改变时,所有视图自动刷新;可以使所有的控制器相互独立工作。

比如实现一个模型、两个视图和一个控制器的程序。当用户在图形化用户界面输入一个球体的半径,程序除显示该球体的体积与表面积外,还将图形化显示该球体。该程序的4个类之间的示意图如下:

a4a6b3f484355703c627c58e514d8aac.png

图1-2  一个模型、两个视图和一个控制器的基本结构

MVC的优点:

(1)最重要的是应该有多个视图对应一个模型的能力。在目前用户需求的快速变化下,可能有多种方式访问应用的要求。例如,订单模型可能有本系统的订单,也有网上订单,或者其他系统的订单,但对于订单的处理都是一样,也就是说订单的处理是一致的。按MVC设计模式,一个订单模型以及多个视图即可解决问题。这样减少了代码的复制,即减少了代码的维护量,一旦模型发生改变,也易于维护。 其次,由于模型返回的数据不带任何显示格式,因而这些模型也可直接应用于接口的使用。

(2)由于一个应用被分离为三层,因此有时改变其中的一层就能满足应用的改变。一个应用的业务流程或者业务规则的改变只需改动MVC的模型层。

(3)控制层的概念也很有效,由于它把不同的模型和不同的视图组合在一起完成不同的请求,因此,控制层可以说是包含了用户请求权限的概念。

(4)它还有利于软件工程化管理。由于不同的层各司其职,每一层不同的应用具有某些相同的特征,有利于通过工程化、工具化产生管理程序代码。

MVC的不足体现在以下几个方面:

(1)增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。

(2)视图与控制器间的过于紧密的连接。视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。

(3)视图对模型数据的低效率访问。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。

(4) 目前,一般高级的界面工具或构造器不支持MVC模式。改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,从而造成使用MVC的困难。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值