MVC,MVP設計模式

什麼是MVP

  MVP是模型(Model)、視圖(View)、主持人(Presenter)的縮寫,分別表明項目中3個不一樣的模塊。html

  模型(Model):負責處理數據的加載或者存儲,好比從網絡或本地數據庫獲取數據等;java

  視圖(View):負責界面數據的展現,與用戶進行交互;android

  主持人(Presenter):至關於協調者,是模型與視圖之間的橋樑,將模型與視圖分離開來。git

  以下圖所示,View與Model並不直接交互,而是使用Presenter做爲View與Model之間的橋樑。其中Presenter中同時持有Viwe層以及Model層的Interface的引用,而View層持有Presenter層Interface的引用。當View層某個界面須要展現某些數據的時候,首先會調用Presenter層的某個接口,而後Presenter層會調用Model層請求數據,當Model層數據加載成功以後會調用Presenter層的回調方法通知Presenter層數據加載完畢,最後Presenter層再調用View層的接口將加載後的數據展現給用戶。這就是MVP模式的整個核心過程。github

  這樣分層的好處就是大大減小了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的困難。
相關文章
相關標籤/搜索