JavaScript MVP模式

2018-08-02 16:26 更新

MVP

模型-視圖-展示器(MVP)是MVC設(shè)計模式的一個衍生模式,它專注于提升展現(xiàn)邏輯.它來自于上個世紀(jì)九十年代早期的一個叫做Taligent的公司,當(dāng)時他們正工作于一個基于C++ CommonPoint環(huán)境的模型.而MVC和MVP的目標(biāo)都直指對整個多組件關(guān)注點的分離,它們之間有一些基礎(chǔ)上的不同。

為了要做出總結(jié)的目的,我們將專注于最適合于基于Web架構(gòu)的MVP版本。

模型,視圖&展示器

MVP中的P代表展示器.它是一個包含視圖的用戶界面邏輯的組件.不像MVC,來自視圖的調(diào)用被委派給了展示器,它是從視圖中解耦出來的,并且轉(zhuǎn)而通過一個接口來同它進行對話.這允許所有類型的有用的東西,比如在單元測試中模擬視圖的調(diào)用。

對MVP最通常的實現(xiàn)是使用一個被動視圖(Passive View 一種對所有動機和目的保持靜默的視圖),包含很少甚至與沒有任何邏輯.如果MVC和MVP是不同的,那是因為其C和P干了不同的事情.在MVP中,P觀察著模型并且當(dāng)模型發(fā)生改變的時候?qū)σ晥D進行更新.P切實的將模型綁定到了視圖,這一責(zé)任在MVC中被控制器提前持有了。

通過視圖發(fā)送請求,展示者執(zhí)行所有和用戶請求相關(guān)的工作,并且把數(shù)據(jù)返回給視圖。從這個方面來講,它們獲取數(shù)據(jù),操作數(shù)據(jù),然后決定數(shù)據(jù)如何在視圖上面展示。在一些實現(xiàn)當(dāng)中,展示者同時和一個服務(wù)層交互,用于持久化數(shù)據(jù)(模型)。模型可以觸發(fā)事件,但是是由展示者扮演這個角色,用于訂閱這些事件,從而來更新視圖。在這個被動體系架構(gòu)下,我們沒有直接數(shù)據(jù)綁定的概念。視圖暴露setter ,而展示者使用這些setter 來設(shè)置數(shù)據(jù)。

相較于MVC模式的這個改變所帶來的好處是,增強了我們應(yīng)用的可測試性,并且提供了一個更加干凈的視圖和模型之間的隔離。但是在這個模式里面伴隨著缺乏數(shù)據(jù)綁定支持的缺陷,這就意味著必須對這個任務(wù)做另外的處理。

盡管被動視圖實現(xiàn)起來普遍都是為視圖和實現(xiàn)一個接口,但在它之上還是有差異的,包括可以更多的把視圖從展示器解耦的事件的使用。由于在Javascript中我們并沒有接口的構(gòu)造,我們這里更多的是使用一種約定而不是一個明確的接口。技術(shù)上看它仍然是一個接口,而從那個角度對于我們而言把它作為一個接口引用可能更加說得過去一些。

也有一種叫做監(jiān)督控制器的MVP的變種,它更加接近于MVC和MVVM模式,因為它提供了來自于直接來源于視圖的模型的數(shù)據(jù)綁定。鍵值觀察(KVO)插件(比如Derick Bailey的Backbone.ModelBingding插件)趨向于吧Backbone帶出被動視圖的范疇,而更多的帶入監(jiān)督控制器和MVVM變異中。

MVP還是MVC?

MVP一般最常使用在企業(yè)級應(yīng)用程序中,這樣的程序中有必要對展現(xiàn)邏輯盡可能的重用。帶有非常復(fù)雜的邏輯和大量用戶交互的應(yīng)用程序中,我們也許會發(fā)現(xiàn)MVC相對來說并不怎么滿足需求,因為要解決這個問題可能意味著對多重控制器的重度依賴。在MVP中,所有這些復(fù)雜的邏輯能夠被封裝到一個展示器中,它可以顯著的簡化維護工作量。

由于MVP的視圖是通過一個接口來被定義的,而這個接口在技術(shù)上唯一的要點只是系統(tǒng)和視圖(展示器除外)之間接觸,這一模式也允許開發(fā)者不需要等待設(shè)計師為應(yīng)用程序制作出布局和圖形,就可以開始編寫展現(xiàn)邏輯。

根據(jù)其實現(xiàn),MVP也許MVC更加容易進行自動的單元測試。為此常常被提及的理由是展示器可以被當(dāng)做用戶接口的完全模擬來使用,而因此它能夠獨立于其它組件接受單元測試。在我的經(jīng)驗中這取決于我們正在實現(xiàn)的MVP所使用的語言(超過一種取代Javascript來實現(xiàn)MVP的可選語言,同Javascript有著相當(dāng)大的不同,比如說ASP.net)。

在一天的終點,我們對MVC可能會有的底層關(guān)注,可能將是保持對MVP的認可,因為它們之間的不同主要是在語義上的。一旦我們對清晰分離的關(guān)注被納入到模型、視圖和控制器(或者展示器)中,我們也許會獲得大部分同樣的好處,而不用去管我們所作出的選擇的差異。

MVC, MVP 和 Backbone.js

很少有,但是如果有任何架構(gòu)性質(zhì)的Javascript框架聲稱用其經(jīng)典形式實現(xiàn)了MVC或者MVP模式的話,那是因為許多開發(fā)者并不認為MVC和MVP是相互沖突的(看到諸如ASP.net或者GWT這樣的web框架,我們實際上更加可能會認為MVP被嚴格的實現(xiàn)了)。這是因為讓我們的應(yīng)用程序有一個附加的展示器/視圖邏輯,同時也仍然當(dāng)其是一種MVC的意味,是有可能的。

Backbone貢獻者Irene Ros(位于波士頓的Bocoup)贊同這種想法,當(dāng)她將視圖分離到屬于它們自己的單獨組件中時,她需要某些東西來實際為她組裝它們。這可以是一個控制器路由(比如Backbone.Router,在本書的后面會提到)或者一個對被獲取數(shù)據(jù)做出響應(yīng)的回調(diào)。

這就是說,一些開發(fā)者確實感覺Backbone.js更加適合于MVP的描述,相比于MVC。他們的觀點是:

  • 相比于控制器,MVP中的展示器更好的描述了Backbone.View(視圖模板和綁定在視圖模板之上的數(shù)據(jù)之間的中間層)。
  • 模型適合Backbone.Model(相較于MVC中的模型并沒有很大的不同)。
  • 視圖最能代表模板(比如 Handlebars/Mustache標(biāo)記模板)

對此的回應(yīng)會是視圖也可以是一個View(如MVC),因為Backbone對于讓它用于多用途有足夠的彈性。MVC中的V和MVP中的P都能夠通過Backbone.View來完成,因為它們能夠達成兩個目標(biāo):都用來渲染原子組件,還有將那個組件組裝起來讓其它視圖來渲染。

我們也已經(jīng)看到Backbone中控制器的責(zé)任Backbone.View和Backbone.Router都有分享,而在下面的示例中我們能夠?qū)嶋H看到那方面實際上都是千真萬確的。

在this.model.bind("change",..)一行中,我們的BackbonePhotoView使用了觀察者模式來對視圖的改變進行“訂閱”。它也處理render()方法中的模板,但是并不像一些其它的實現(xiàn),用戶交互也在視圖中處理(見events參數(shù))。

var PhotoView = Backbone.View.extend({

    //... is a list tag.
    tagName:  "li",

    // Pass the contents of the photo template through a templating
    // function, cache it for a single photo
    template: _.template( $("#photo-template").html() ),

    // The DOM events specific to an item.
    events: {
      "click img" : "toggleViewed"
    },

    // The PhotoView listens for changes to
    // its model, re-rendering. Since tHere's
    // a one-to-one correspondence between a
    // **Photo** and a **PhotoView** in this
    // app, we set a direct reference on the model for convenience.

    initialize: function() {
      this.model.on( "change", this.render, this );
      this.model.on( "destroy", this.remove, this );
    },

    // Re-render the photo entry
    render: function() {
      $( this.el ).html( this.template(this.model.toJSON() ));
      return this;
    },

    // Toggle the `"viewed"` state of the model.
    toggleViewed: function() {
      this.model.viewed();
    }

});

另一種(完全不同的)看法是Backbone更加向我們前面考察過的Smalltalk-80MVC靠攏。

定期為Backbone寫博客的Derick Bailey之前已經(jīng)提到過,最終最好不要去強迫Backbone讓其適應(yīng)任何特定的設(shè)計模式。設(shè)計模式應(yīng)該考慮指導(dǎo)可能如何被構(gòu)建的靈活性,而在這一方面,Backbone既不適應(yīng)MVC,也不適應(yīng)MVP。相反,它從多個架構(gòu)模式中借用了一些最好的經(jīng)驗而創(chuàng)造出了一個靈活的框架,并且工作得很好。

而理解這些這些概念源自哪里和為什么源自那里是值得去做的,因此我希望我對于MVC和MVP的闡述對此已經(jīng)有所幫助。就叫它Backbone方法吧,MV*或帶有的其它應(yīng)用程序架構(gòu)的意味。大多數(shù)結(jié)構(gòu)性Javascript框架自主決定自身采用的經(jīng)典模式,不管是有意還是無意為之的,最重要的是它們幫助了我們有組織,干凈的來開發(fā)方便維護的應(yīng)用程序。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號