zoukankan      html  css  js  c++  java
  • 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相关联,并处理触发模型值改变的事件。

     1 import java.util.Observable;  
     2   
     3 //Sphere.java:Model类  
     4 //必须继承Observable,在Observable类中,方法addObserver()将视图与模型相关联  
     5 class Sphere extends Observable {  
     6   
     7     private double myRadius;  
     8       
     9     public void setRadius(double r) {  
    10         myRadius = r;  
    11         this.setChanged();         //指示模型已经改变  
    12         this.notifyObservers();    //通知各个视图,从父继承的方法  
    13     }  
    14     //......  
    15 }  
     1 import java.util.Observable;  
     2 import java.util.Observer;  
     3 import javax.swing.JPanel;  
     4   
     5 //TextView.java:View视图类  
     6 //当模型Sphere类的状态发生改变时,与模型相关联的视图中的update()方法  
     7 //就会自动被调用,从而实现视图的自动刷新  
     8 public class TextView extends JPanel implements Observer {  
     9   
    10     @Override  
    11     public void update(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 import java.awt.Container;  
     2 import java.awt.event.ActionEvent;  
     3 import javax.swing.JFrame;  
     4 import javax.swing.JTextField;  
     5   
     6 // SphereWindow.java:Controller类  
     7 // 它主要新建Model与View,将view与Mode相关联,并处理事件  
     8 public class SphereWindow extends JFrame {  
     9   
    10     public SphereWindow() {  
    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 void actionPerformed(ActionEvent e) {  
    23         JTextField t = (JTextField) e.getSource();  
    24         double r = Double.parseDouble(t.getText());  
    25         model.setRadius(r);  
    26     }  
    27     //......  
    28 }  

    这种MVC模式的程序具有极其良好的可扩展性。它可以轻松实现一个模型的多个视图;可以采用多个控制器;可以实现当模型改变时,所有视图自动刷新;可以使所有的控制器相互独立工作。
      比如实现一个模型、两个视图和一个控制器的程序。当用户在图形化用户界面输入一个球体的半径,程序除显示该球体的体积与表面积外,还将图形化显示该球体。该程序的4个类之间的示意图如下:


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

      

    MVC的优点:
      (1)最重要的是应该有多个视图对应一个模型的能力。
    在目前用户需求的快速变化下,可能有多种方式访问应用的要求。例如,订单模型可能有本系统的订单,也有网上订单,或者其他系统的订单,但对于订单的处理都是一样,也就是说订单的处理是一致的。按MVC设计模式,一个订单模型以及多个视图即可解决问题。这样减少了代码的复制,即减少了代码的维护量,一旦模型发生改变,也易于维护。 其次,由于模型返回的数据不带任何显示格式,因而这些模型也可直接应用于接口的使用。
      (2)由于一个应用被分离为三层,因此有时改变其中的一层就能满足应用的改变。一个应用的业务流程或者业务规则的改变只需改动MVC的模型层。
      (3)控制层的概念也很有效,由于它把不同的模型和不同的视图组合在一起完成不同的请求,因此,控制层可以说是包含了用户请求权限的概念。
      (4)它还有利于软件工程化管理。由于不同的层各司其职,每一层不同的应用具有某些相同的特征,有利于通过工程化、工具化产生管理程序代码。
      MVC的不足体现在以下几个方面:
      (1)增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。
      (2)视图与控制器间的过于紧密的连接。视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。
      (3)视图对模型数据的低效率访问。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。
      (4) 目前,一般高级的界面工具或构造器不支持MVC模式。改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,从而造成使用MVC的困难。
  • 相关阅读:
    15. DML, DDL, LOGON 触发器
    5. 跟踪标记 (Trace Flag) 834, 845 对内存页行为的影响
    4. 跟踪标记 (Trace Flag) 610 对索引组织表(IOT)最小化日志
    14. 类似正则表达式的字符处理问题
    01. SELECT显示和PRINT打印超长的字符
    3. 跟踪标记 (Trace Flag) 1204, 1222 抓取死锁信息
    2. 跟踪标记 (Trace Flag) 3604, 3605 输出DBCC命令结果
    1. 跟踪标记 (Trace Flag) 1117, 1118 文件增长及空间分配方式
    0. 跟踪标记 (Trace Flag) 简介
    SpringBoot + Redis + Shiro 实现权限管理(转)
  • 原文地址:https://www.cnblogs.com/Im-Victor/p/6295329.html
Copyright © 2011-2022 走看看