本文將從收集來的資料整理分析MVC模型的各類應用以及其演化歷程html
1、 介紹 MV* 模式前端
MVC、MVP、MVVM 模式都是爲了解決圖形界面應用程序複雜性管理問題而產生的應用架構模式。追根溯源,從最經典的Smalltalk-80 MVC模式開始逐步還原圖形界面之下最真實的MV*模式。java
圖形界面的應用程序提供給用戶可視化的操做界面,這個界面提供給數據和信息。用戶輸入行爲(鍵盤,鼠標等)會執行一些業務邏輯,可能會致使對應用程序數據的變動,數據的變動天然須要用戶界面的同步變動以提供最準確的信息。例如用戶對一個電子表格從新排序的操做,應用程序須要響應用戶操做,對數據進行排序,而後須要同步到界面上。web
在開發應用程序的時候,以求更好的管理應用程序的複雜性,基於職責分離(Speration of Duties)的思想都會對應用程序進行分層。在開發圖形界面應用程序的時候,會把管理用戶界面的層次稱爲View,應用程序的數據爲Model(注意這裏的Model指的是Domain Model,這個應用程序對須要解決的問題的數據抽象,不包含應用的狀態,能夠簡單理解爲對象)。Model層對應用程序的業務邏輯無知,只保存數據結構和提供數據操做的接口。spring
有了View和Model的分層,那麼就有了兩個問題:數據庫
帶着這兩個問題開始探索MV*模式,會發現這些模式之間的差別能夠概括爲對這兩個問題處理的方式的不一樣。而幾乎全部的MV*模式都是經典的Smalltalk-80 MVC的修改版。apache
早在上個世紀70年代,美國的施樂公司(Xerox)的工程師研發了Smalltalk編程語言,而且開始用它編寫圖形界面的應用程序。而在Smalltalk-80這個版本的時候,一位叫Trygve Reenskaug的工程師設計了MVC圖形應用程序的架構模式,極大地下降了圖形應用程序的管理難度。而在四人衆(GoF)的設計模式當中並無把MVC當作是設計模式,而僅僅是把它當作解決問題的一些類的集合。Smalltalk-80 MVC和GoF描述的MVC是最經典的MVC模式。編程
MVC出了把應用程序分紅View、Model層,還額外的加了一個Controller層,它的職責就是專門管理應用程序的業務邏輯。Model、View、Controller三個層次的依賴關係以下:設計模式
Controller和View都依賴Model層,Controller和View能夠互相依賴。在一些網上的資料Controller和View之間的依賴關係可能不同,有些是單向依賴,有些是雙向依賴,這個其實關係不大,後面會看到它們的依賴關係都是爲了把處理用戶行爲觸發的業務邏輯的處理權交給Controller。瀏覽器
用戶的對View操做之後,View捕獲到這個操做,會把處理的權利交移給Controller(Pass calls);Controller接着會執行相關的業務邏輯,這些業務邏輯可能須要對Model進行相應的操做;當Model變動了之後,會經過觀察者模式(Observer Pattern)通知View;View經過觀察者模式收到Model變動的消息之後,會向Model請求最新的數據,而後從新更新界面。以下圖:
看似沒有什麼特別的地方,可是由幾個須要特別關注的關鍵點:
須要特別注意的是MVC模式的精髓在於第三點:Model的更新是經過觀察者模式告知View的,具體表現形式能夠是Pub/Sub或者是觸發Events。而網上不少對於MVC的描述都沒有強調這一點。經過觀察者模式的好處就是:不一樣的MVC三角關係可能會有共同的Model,一個MVC三角中的Controller操做了Model之後,兩個MVC三角的View都會接受到通知,而後更新本身。保持了依賴同一塊Model的不一樣View顯示數據的實時性和準確性。咱們天天都在用的觀察者模式,在幾十年前就已經被大神們整合到MVC的架構當中。
這裏有一個MVC模式的JavaScript Demo,實現了一個小的TodoList應用程序。經典的Smalltalk-80 MVC不須要任何框架支持就能夠實現。目前Web前端框架當中只有一個號稱是嚴格遵循Smalltalk-80 MVC模式的:maria.js。
優勢:
缺點:
在Web服務端開發的時候也會接觸到MVC模式,而這種MVC模式不能嚴格稱爲MVC模式。經典的MVC模式只是解決客戶端圖形界面應用程序的問題,而對服務端無效。服務端的MVC模式又本身特定的名字:MVC Model 2,或者叫JSP Model 2,或者直接就是Model 2 。Model 2客戶端服務端的交互模式以下:
服務端接收到來自客戶端的請求,服務端經過路由規則把這個請求交由給特定的Controller進行處理,Controller執行相應的業務邏輯,對數據庫數據(Model)進行操做,而後用數據去渲染特定的模版,返回給客戶端。
由於HTTP協議是單工協議而且是無狀態的,服務器沒法直接給客戶端推送數據。除非客戶端再次發起請求,不然服務器端的Model的變動就沒法告知客戶端。因此能夠看到經典的Smalltalk-80 MVC中Model經過觀察者模式告知View更新這一環被無情地打破,不能稱爲嚴格的MVC。
Model 2模式最先在1998年應用在JSP應用程序當中,JSP Model 1應用管理的混亂誘發了JSP參考了客戶端MVC模式,催生了Model 2。
後來這種模式幾乎被應用在全部語言的Web開發框架當中。PHP的ThinkPHP,Python的Dijango、Flask,NodeJS的Express,Ruby的RoR,基本都採納了這種模式。日常所講的MVC基本是這種服務端的MVC。
MVP模式有兩種:
而大多數狀況下討論的都是Passive View模式。本文會對PV模式進行較爲詳細的介紹,而SC模式則簡單說起。
MVP模式是MVC模式的改良。在上個世紀90年代,IBM旗下的子公司Taligent在用C/C++開發一個叫CommonPoint的圖形界面應用系統的時候提出來的。
MVP模式把MVC模式中的Controller換成了Presenter。MVP層次之間的依賴關係以下:
MVP打破了View原來對於Model的依賴,其他的依賴關係和MVC模式一致。
既然View對Model的依賴被打破了,那View如何同步Model的變動?看看MVP的調用關係:
和MVC模式同樣,用戶對View的操做都會從View交移給Presenter。Presenter一樣的會執行相應的業務邏輯,而且對Model進行相應的操做;而這時候Model也是經過觀察者模式把本身變動的消息傳遞出去,可是是傳給Presenter而不是View。Presenter獲取到Model變動的消息之後,經過View提供的接口更新界面。
關鍵點:
對比在MVC中,Controller是不能操做View的,View也沒有提供相應的接口;而在MVP當中,Presenter能夠操做View,View須要提供一組對界面操做的接口給Presenter進行調用;Model仍然經過事件廣播本身的變動,但由Presenter監聽而不是View。
MVP模式,這裏也提供一個用JavaScript編寫的例子。
優勢:
缺點:
上面講的是MVP的Passive View模式,該模式下View很是Passive,它幾乎什麼都不知道,Presenter讓它幹什麼它就幹什麼。而Supervising Controller模式中,Presenter會把一部分簡單的同步邏輯交給View本身去作,Presenter只負責比較複雜的、高層次的UI操做,因此能夠把它當作一個Supervising Controller。
Supervising Controller模式下的依賴和調用關係:
由於Supervising Controller用得比較少,對它的討論就到這裏爲止。
MVVM能夠看做是一種特殊的MVP(Passive View)模式,或者說是對MVP模式的一種改良。
MVVM模式最先是微軟公司提出,而且了大量使用在.NET的WPF和Sliverlight中。2005年微軟工程師John Gossman在本身的博客上首次公佈了MVVM模式。
MVVM表明的是Model-View-ViewModel,這裏須要解釋一下什麼是ViewModel。ViewModel的含義就是 "Model of View",視圖的模型。它的含義包含了領域模型(Domain Model)和視圖的狀態(State)。 在圖形界面應用程序當中,界面所提供的信息可能不只僅包含應用程序的領域模型。還可能包含一些領域模型不包含的視圖狀態,例如電子表格程序上須要顯示當前排序的狀態是順序的仍是逆序的,而這是Domain Model所不包含的,但也是須要顯示的信息。
能夠簡單把ViewModel理解爲頁面上所顯示內容的數據抽象,和Domain Model不同,ViewModel更適合用來描述View。
MVVM的依賴關係和MVP依賴,只不過是把P換成了VM。
MVVM的調用關係和MVP同樣。可是,在ViewModel當中會有一個叫Binder,或者是Data-binding engine的東西。之前所有由Presenter負責的View和Model之間數據同步操做交由給Binder處理。你只須要在View的模版語法當中,指令式地聲明View上的顯示的內容是和Model的哪一塊數據綁定的。當ViewModel對進行Model更新的時候,Binder會自動把數據更新到View上去,當用戶對View進行操做(例如表單輸入),Binder也會自動把數據更新到Model上去。這種方式稱爲:Two-way data-binding,雙向數據綁定。能夠簡單而不恰當地理解爲一個模版引擎,可是會根據數據變動實時渲染。
也就是說,MVVM把View和Model的同步邏輯自動化了。之前Presenter負責的View和Model同步再也不手動地進行操做,而是交由框架所提供的Binder進行負責。只須要告訴Binder,View顯示的數據對應的是Model哪一部分便可。
這裏有一個JavaScript MVVM的例子,由於MVVM須要Binder引擎。因此例子中使用了一個MVVM的庫:Vue.js。
優勢:
缺點:
能夠看到,從MVC->MVP->MVVM,就像一個打怪升級的過程。後者解決了前者遺留的問題,把前者的缺點優化成了優勢。一樣的Demo功能,代碼從最開始的一堆文件,優化成了最後只須要20幾行代碼就完成。MV*模式之間的區分仍是蠻清晰的,但願能夠給對這些模式理解比較模糊的同窗帶來一些參考和思路。
2、Web MVC簡介
在Web世界裏,具體步驟以下:
一、 Web瀏覽器(如IE)發起請求,如訪問百度
二、 Web服務器(如Tomcat)接收請求,處理請求(好比用戶新增,則將把用戶保存一下),最後產生響應(通常爲html)。
三、web服務器處理完成後,返回內容給web客戶端(通常就是咱們的瀏覽器),客戶端對接收的內容進行處理(如web瀏覽器將會對接收到的html內容進行渲染以展現給客戶)。
所以,在Web世界裏:
都是Web客戶端發起請求,Web服務器接收、處理併產生響應。
通常Web服務器是不能主動通知Web客戶端更新內容。雖然如今有些技術如服務器推(如Comet)、還有如今的HTML5 websocket能夠實現Web服務器主動通知Web客戶端。
到此咱們瞭解了在web開發時的請求/響應模型,接下來咱們看一下標準的MVC模型是什麼。
MVC模型:是一種架構型的模式,自己不引入新功能,只是幫助咱們將開發的結構組織的更加合理,使展現與模型分離、流程控制邏輯、業務邏輯調用與展現邏輯分離。如圖1-2
圖1-2
首先讓咱們瞭解下MVC(Model-View-Controller)三元組的概念:
Model(模型):數據模型,提供要展現的數據,所以包含數據和行爲,能夠認爲是領域模型或JavaBean組件(包含數據和行爲),不過如今通常都分離開來:Value Object(數據) 和 服務層(行爲)。也就是模型提供了模型數據查詢和模型數據的狀態更新等功能,包括數據和業務。
View(視圖):負責進行模型的展現,通常就是咱們見到的用戶界面,客戶想看到的東西。
Controller(控制器):接收用戶請求,委託給模型進行處理(狀態改變),處理完畢後把返回的模型數據返回給視圖,由視圖負責展現。 也就是說控制器作了個調度員的工做,。
從圖1-1咱們還看到,在標準的MVC中模型能主動推數據給視圖進行更新(觀察者設計模式,在模型上註冊視圖,當模型更新時自動更新視圖),但在Web開發中模型是沒法主動推給視圖(沒法主動更新用戶界面),由於在Web開發是請求-響應模型。
那接下來咱們看一下在Web裏MVC是什麼樣子,咱們稱其爲 Web MVC 來區別標準的MVC。
模型-視圖-控制器概念和標準MVC概念同樣,請參考1.2,咱們再看一下Web MVC標準架構,如圖1-3:
如圖1-3
在Web MVC模式下,模型沒法主動推數據給視圖,若是用戶想要視圖更新,須要再發送一次請求(即請求-響應模型)。
概念差很少了,咱們接下來了解下Web端開發的發展歷程,和使用代碼來演示一下Web MVC是如何實現的,還有爲何要使用MVC這個模式呢?
此處咱們只是簡單的敘述比較核心的歷程,如圖1-4
圖1-4
1.4.1、CGI:(Common Gateway Interface)公共網關接口,一種在web服務端使用的腳本技術,使用C或Perl語言編寫,用於接收web用戶請求並處理,最後動態產生響應給用戶,但每次請求將產生一個進程,重量級。
1.4.2、Servlet:一種JavaEE web組件技術,是一種在服務器端執行的web組件,用於接收web用戶請求並處理,最後動態產生響應給用戶。但每次請求只產生一個線程(並且有線程池),輕量級。並且能利用許多JavaEE技術(如JDBC等)。本質就是在java代碼裏面 輸出 html流。但表現邏輯、控制邏輯、業務邏輯調用混雜。如圖1-5
圖1-5
如圖1-5,這種作法是絕對不可取的,控制邏輯、表現代碼、業務邏輯對象調用混雜在一塊兒,最大的問題是直接在Java代碼裏面輸出Html,這樣前端開發人員沒法進行頁面風格等的設計與修改,即便修改也是很麻煩,所以實際項目這種作法不可取。
1.4.3、JSP:(Java Server Page):一種在服務器端執行的web組件,是一種運行在標準的HTML頁面中嵌入腳本語言(如今只支持Java)的模板頁面技術。本質就是在html代碼中嵌入java代碼。JSP最終仍是會被編譯爲Servlet,只不過比純Servlet開發頁面更簡單、方便。但表現邏輯、控制邏輯、業務邏輯調用仍是混雜。如圖1-6
圖1-6
如圖1-6,這種作法也是絕對不可取的,控制邏輯、表現代碼、業務邏輯對象調用混雜在一塊兒,但比直接在servlet裏輸出html要好一點,前端開發人員能夠進行簡單的頁面風格等的設計與修改(但若是嵌入的java腳本太多也是很難修改的),所以實際項目這種作法不可取。
JSP本質仍是Servlet,最終在運行時會生成一個Servlet(如tomcat,將在tomcat\work\Catalina\web應用名\org\apache\jsp下生成),但這種使得寫html簡單點,但還是控制邏輯、表現代碼、業務邏輯對象調用混雜在一塊兒。
1.4.4、Model1:能夠認爲是JSP的加強版,能夠認爲是jsp+javabean如圖1-7
特色:使用<jsp:useBean>標準動做,自動將請求參數封裝爲JavaBean組件;還必須使用java腳本執行控制邏輯。
圖1-7
此處咱們能夠看出,使用<jsp:useBean>標準動做能夠簡化javabean的獲取/建立,及將請求參數封裝到javabean,再看一下Model1架構,如圖1-8。
圖1-8 Model1架構
Model1架構中,JSP負責控制邏輯、表現邏輯、業務對象(javabean)的調用,只是比純JSP簡化了獲取請求參數和封裝請求參數。一樣是很差的,在項目中應該嚴禁使用(或最多再demo裏使用)。
1.4.5、Model2:在JavaEE世界裏,它能夠認爲就是Web MVC模型
Model2架構其實能夠認爲就是咱們所說的Web MVC模型,只是控制器採用Servlet、模型採用JavaBean、視圖採用JSP,如圖1-9
圖1-9 Model2架構
具體代碼事例以下:
從Model2架構能夠看出,視圖和模型分離了,控制邏輯和展現邏輯分離了。
但咱們也看到嚴重的缺點:
1. 一、控制器:
1.1.一、控制邏輯可能比較複雜,其實咱們能夠按照規約,如請求參數submitFlag=toAdd,咱們其實能夠直接調用toAdd方法,來簡化控制邏輯;並且每一個模塊基本須要一個控制器,形成控制邏輯可能很複雜;
1.1.二、請求參數到模型的封裝比較麻煩,若是能交給框架來作這件事情,咱們能夠從中獲得解放;
1.1.三、選擇下一個視圖,嚴重依賴Servlet API,這樣很難或基本不可能更換視圖;
1.1.四、給視圖傳輸要展現的模型數據,使用Servlet API,更換視圖技術也要一塊兒更換,很麻煩。
1.二、模型:
1.2.一、此處模型使用JavaBean,可能形成JavaBean組件類很龐大,通常如今項目都是採用三層架構,而不採用JavaBean。
1.三、視圖
1.3.一、如今被綁定在JSP,很難更換視圖,好比Velocity、FreeMarker;好比我要支持Excel、PDF視圖等等。
1.4.5、服務到工做者:Front Controller + Application Controller + Page Controller + Context
即,前端控制器+應用控制器+頁面控制器(也有稱其爲動做)+上下文,也是Web MVC,只是責任更加明確,詳情請參考《核心J2EE設計模式》和《企業應用架構模式》如圖1-10:
圖1-10
運行流程以下:
職責:
Front Controller:前端控制器,負責爲表現層提供統一訪問點,從而避免Model2中出現的重複的控制邏輯(由前端控制器統一回調相應的功能方法,如前邊的根據submitFlag=login轉調login方法);而且能夠爲多個請求提供共用的邏輯(如準備上下文等等),將選擇具體視圖和具體的功能處理(如login裏邊封裝請求參數到模型,並調用業務邏輯對象)分離。
Application Controller:應用控制器,前端控制器分離選擇具體視圖和具體的功能處理以後,須要有人來管理,應用控制器就是用來選擇具體視圖技術(視圖的管理)和具體的功能處理(頁面控制器/命令對象/動做管理),一種策略設計模式的應用,能夠很容易的切換視圖/頁面控制器,相互不產生影響。
Page Controller(Command):頁面控制器/動做/處理器:功能處理代碼,收集參數、封裝參數到模型,轉調業務對象處理模型,返回邏輯視圖名交給前端控制器(和具體的視圖技術解耦),由前端控制器委託給應用控制器選擇具體的視圖來展現,能夠是命令設計模式的實現。頁面控制器也被稱爲處理器或動做。
Context:上下文,還記得Model2中爲視圖準備要展現的模型數據嗎,咱們直接放在request中(Servlet API相關),有了上下文以後,咱們就能夠將相關數據放置在上下文,從而與協議無關(如Servlet API)的訪問/設置模型數據,通常經過ThreadLocal模式實現。
到此,咱們回顧了整個web開發架構的發展歷程,可能不一樣的web層框架在細節處理方面不一樣,但的目的是同樣的:
乾淨的web表現層:
模型和視圖的分離;
控制器中的控制邏輯與功能處理分離(收集並封裝參數到模型對象、業務對象調用);
控制器中的視圖選擇與具體視圖技術分離。
輕薄的web表現層:
作的事情越少越好,薄薄的,不該該包含無關代碼;
只負責收集並組織參數到模型對象,啓動業務對象的調用;
控制器只返回邏輯視圖名並由相應的應用控制器來選擇具體使用的視圖策略;
儘可能少使用框架特定API,保證容易測試。
到此咱們瞭解Web MVC的發展歷程,接下來讓咱們瞭解下Spring MVC究竟是什麼、架構及來個HelloWorld瞭解下具體怎麼使用吧。
本章具體代碼請參考 springmvc-chapter1工程。
3、MVC和MVP在app中的對比分析以及實際應用
爲了解決邏輯處理和UI視圖的鬆散耦合,MVC和MVP的架構模式在不少App中使用比較普遍。
那什麼是MVP呢?它又和咱們經常聽到的MVC有什麼關係了以及區別呢?
MVP 是從經典的模式MVC演變而來,它們的基本思想有相通的地方:Controller/Presenter負責邏輯的處理,Model提供數據,View負 責顯示。做爲一種新的模式,MVP與MVC有着一個重大的區別:在MVP中View並不直接使用Model,它們之間的通訊是經過Presenter (MVC中的Controller)來進行的,全部的交互都發生在Presenter內部,而在MVC中View會從直接Model中讀取數據而不是經過 Controller。
在MVC裏,View是能夠直接訪問Model的!從而,View裏會包含Model信息,不可避免的還要包括一些業務邏輯。 在MVC模型裏,更關注的Model的不變,而同時有多個對Model的不一樣顯示,及View。因此,在MVC模型裏,Model不依賴於View,可是 View是依賴於Model的。不只如此,由於有一些業務邏輯在View裏實現了,致使要更改View也是比較困難的,至少那些業務邏輯是沒法重用的。
在MVP裏,Presenter徹底把Model和View進行了分離,主要的程序邏輯在Presenter裏實現。並且,Presenter與具 體的View是沒有直接關聯的,而是經過定義好的接口進行交互,從而使得在變動View時候能夠保持Presenter的不變,即重用! 不只如此,咱們還能夠編寫測試用的View,模擬用戶的各類操做,從而實現對Presenter的測試—而不須要使用自動化的測試工具。 咱們甚至能夠在Model和View都沒有完成時候,就能夠經過編寫Mock Object(即實現了Model和View的接口,但沒有具體的內容的)來測試Presenter的邏輯。 在MVP裏,應用程序的邏輯主要在Presenter來實現,其中的View是很薄的一層。所以就有人提出了Presenter First的設計模式,就是根據User Story來首先設計和開發Presenter。在這個過程當中,View是很簡單的,可以把信息顯示清楚就能夠了。在後面,根據須要再隨便更改View, 而對Presenter沒有任何的影響了。 若是要實現的UI比較複雜,並且相關的顯示邏輯還跟Model有關係,就能夠在View和Presenter之間放置一個Adapter。由這個 Adapter來訪問Model和View,避免二者之間的關聯。而同時,由於Adapter實現了View的接口,從而能夠保證與Presenter之 間接口的不變。這樣就能夠保證View和Presenter之間接口的簡潔,又不失去UI的靈活性。 在MVP模式裏,View只應該有簡單的Set/Get的方法,用戶輸入和設置界面顯示的內容,除此就不該該有更多的內容,毫不允許直接訪問Model— 這就是與MVC很大的不一樣之處。
MVP的優勢:
一、模型與視圖徹底分離,咱們能夠修改視圖而不影響模型;
二、能夠更高效地使用模型,由於全部的交互都發生在一個地方——Presenter內部;
三、咱們能夠將一個Presenter用於多個視圖,而不須要改變Presenter的邏輯。這個特性很是的有用,由於視圖的變化老是比模型的變化頻繁;
四、若是咱們把邏輯放在Presenter中,那麼咱們就能夠脫離用戶接口來測試這些邏輯(單元測試)。
一、創建bean
public class UserBean {
private String mFirstName; private String mLastName; public UserBean(String firstName, String lastName) { this. mFirstName = firstName; this. mLastName = lastName; } public String getFirstName() { return mFirstName; } public String getLastName() { return mLastName; } }
二、創建model接口(處理業務邏輯,這裏指數據讀寫)
public interface IUserModel {
void setID(int id); void setFirstName(String firstName); void setLastName(String lastName); int getID(); UserBean load(int id);// 經過id讀取user信息,返回一個UserBean }
三、創建view接口(更新ui中的view狀態),這裏列出須要操做當前view的方法
public interface IUserView {
int getID(); String getFristName(); String getLastName(); void setFirstName(String firstName); void setLastName(String lastName); }
四、創建presenter(主導器,經過iView和iModel接口操做model和view),activity能夠把全部邏輯給presenter處理,這樣java邏輯就從手機的activity中分離出來
public class UserPresenter {
private IUserView mUserView; private IUserModel mUserModel; public UserPresenter(IUserView view) { mUserView = view; mUserModel = new UserModel(); } public void saveUser( int id, String firstName, String lastName) { mUserModel.setID(id); mUserModel.setFirstName(firstName); mUserModel.setLastName(lastName); } public void loadUser( int id) { UserBean user = mUserModel.load(id); mUserView.setFirstName(user.getFirstName()); // 經過調用IUserView的方法來更新顯示 mUserView.setLastName(user.getLastName()); } }
MVP主要解決就是把邏輯層抽出來成P層,要是遇到需求邏輯上的更改就能夠只須要修改P層了或者遇到邏輯上的大概咱們能夠直接從寫一個P也能夠,很 多開發人員把全部的東西都寫在了Activity/Fragment裏面這樣一來遇到頻繁改需求或者邏輯愈來愈複雜的時候,Activity /Fragment裏面就會出現過多的混雜邏輯致使出錯,因此MVP模式對於APP來對控制邏輯和UI的解耦來講是一個不錯的選擇!
4、MVP模式
public class MainActivity extends AppCompatActivity implements ViewInterface, View.OnClickListener {
private EditText content; private EditText author; private Button send; private Button clean; private ProgressBar loading; private PresenterImpl presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } public void initView(){ content = (EditText) findViewById(R.id.content); author = (EditText) findViewById(R.id.author); loading = (ProgressBar) findViewById(R.id.loading); send = (Button) findViewById(R.id.send); clean = (Button) findViewById(R.id.clean); send.setOnClickListener(this); clean.setOnClickListener(this); presenter = new PresenterImpl(this); } /** * 顯示和隱藏進度條 */ @Override public void showProgressBar() { loading.setVisibility(View.VISIBLE); } @Override public void hideProgressBar() { loading.setVisibility(View.GONE); } /** * 發送是成功仍是失敗各自須要作的事情 */ @Override public void sendSuccess() { Toast.makeText(this,"發送成功!",Toast.LENGTH_SHORT).show(); } @Override public void sendFailed() { Toast.makeText(this,"發送失敗!",Toast.LENGTH_SHORT).show(); } /** * 清除發送內容 */ @Override public void cleanContent() { content.setText(""); } @Override public void cleanAuthor() { author.setText(""); } /** * 獲取內容和做者 */ @Override public String getContent() { return content.getText().toString().trim(); } @Override public String getAuthor() { return author.getText().toString().trim(); } /** * Called when a view has been clicked. * @param v The view that was clicked. */ @Override public void onClick(View v) { switch( v.getId()){ case R.id.send: presenter.send(); break; case R.id.clean: presenter.clean(); break; } } @Override protected void onDestroy() { presenter.destroy(); super.onDestroy(); } }
public class MainActivity extends AppCompatActivity implements ViewInterface, View.OnClickListener {
private EditText content; private EditText author; private Button send; private Button clean; private ProgressBar loading; private PresenterImpl presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } public void initView(){ content = (EditText) findViewById(R.id.content); author = (EditText) findViewById(R.id.author); loading = (ProgressBar) findViewById(R.id.loading); send = (Button) findViewById(R.id.send); clean = (Button) findViewById(R.id.clean); send.setOnClickListener(this); clean.setOnClickListener(this); presenter = new PresenterImpl(this); } /** * 顯示和隱藏進度條 */ @Override public void showProgressBar() { loading.setVisibility(View.VISIBLE); } @Override public void hideProgressBar() { loading.setVisibility(View.GONE); } /** * 發送是成功仍是失敗各自須要作的事情 */ @Override public void sendSuccess() { Toast.makeText(this,"發送成功!",Toast.LENGTH_SHORT).show(); } @Override public void sendFailed() { Toast.makeText(this,"發送失敗!",Toast.LENGTH_SHORT).show(); } /** * 清除發送內容 */ @Override public void cleanContent() { content.setText(""); } @Override public void cleanAuthor() { author.setText(""); } /** * 獲取內容和做者 */ @Override public String getContent() { return content.getText().toString().trim(); } @Override public String getAuthor() { return author.getText().toString().trim(); } /** * Called when a view has been clicked. * @param v The view that was clicked. */ @Override public void onClick(View v) { switch( v.getId()){ case R.id.send: presenter.send(); break; case R.id.clean: presenter.clean(); break; } } @Override protected void onDestroy() { presenter.destroy(); super.onDestroy(); } }
*
* 控制 業務層,處理全部的業務邏輯,鏈接view 和 model
*/
public interface PresenterInterface { public void send(); public void destroy(); public void clean(); }
public class PresenterImpl implements PresenterInterface {
ViewInterface viewInterface;
ModelImpl model;
private Handler mHandler = new Handler(); public PresenterImpl() { } public PresenterImpl(ViewInterface viewInterface) { this.viewInterface = viewInterface; model = new ModelImpl(); } @Override public void send() { //顯示進度條 viewInterface.showProgressBar(); model.send(viewInterface.getContent(), viewInterface.getAuthor(), new ModelInterface.OnSendLinterer() { @Override public void sendSuccess() { mHandler.post(new Runnable() { @Override public void run() { viewInterface.sendSuccess(); viewInterface.hideProgressBar(); } }); } @Override public void sendFailed() { mHandler.post(new Runnable() { @Override public void run() { viewInterface.sendFailed(); viewInterface.hideProgressBar(); } }); } }); } @Override public void destroy() { viewInterface = null; model.destory(); model = null; } @Override public void clean() { viewInterface.cleanAuthor(); viewInterface.cleanContent(); } }
/*
* 數據層,作的事情就是爲Presenter層提供數據
*/
public interface ModelInterface {
interface OnSendLinterer{ void sendSuccess(); void sendFailed(); } public void send(String content, String author, OnSendLinterer onSendLinterer); }
public class ModelImpl implements ModelInterface {
private ExecutorService executorService ; public ModelImpl(){ //獲取一個線程池 根據 虛擬機可用的處理器的最大數量 +1 定義線程池大小 executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()+1); } @Override public void send(String content, String author,final OnSendLinterer onSendLinterer) { executorService.execute(new Runnable() { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see Thread#run() */ @Override public void run() { //模擬網絡請求的耗時操做 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //隨機boolean 模擬發送成功或失敗 Random random = new Random(); if( random.nextBoolean() ){ onSendLinterer.sendSuccess(); }else{ onSendLinterer.sendFailed(); } } }); //再也不接受新的任務 // executorService.shutdown(); /*try { future.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }*/ } public void destory(){ executorService = null; } }