記得剛進公司的時候,咱們除了作常規的Training Project外,天天還要上課,接受各類技術培訓和公司業務介紹。當時第一次知道QA和SQA的區別。Training Project時間其實比較緊張,給咱們的就是一個英文的需求文檔。咱們要作的就是數據庫設計、結構文檔、用例文檔、項目搭建、代碼編寫、單元測試,每一個階段Leader會Review。除此以外,還要E-R圖、時序圖、用例圖。麻將雖小,五臟俱全。哦,對了,全部輸出必須英文完成。最後要求項目能一鍵安裝使用。html
不感興趣?請直接跳到第二部分,哈哈~android
Training Project其實就是作一個購物網站,作一個WinForm,最後用上WCF。對我來講,不是什麼大問題,在學校已經作過相似的了。Scott Mitchell 60多篇的ASP.NET教程被我打印出厚厚的四本小書,代碼從頭敲了個遍。學完後就基本熟悉ASP.NET主流控件的使用,明白數據綁定、緩存以及三層架構的做用了。那麼這時候,你能夠達到初級的水平了。想進一步提高本身,就要懂得必定的代碼封裝、自定義控件、理解Pager的生命週期等等。關於書籍,理論方面我的推薦《你必須知道的.NET》、Jeffrey Richter《深刻理解 .NET》和《Windows核心編程》。博客園內也有Scott Mitchell文章的中文翻譯:Scott Mitchell的ASP.NET2.0數據指南中文版索引。上個圖懷念一下:git
因此個人Training Project基本就是這個風格。固然,還有鼎鼎大名的PetShop。當時的水平,也就勉強明白抽象工廠,但這個寵物商店的架構圖是長這樣的:github
震住菜鳥有沒有?Talk is cheap, show me the code。廢話少說,放碼過來!代碼下載下來,光是Library就近20個,菜鳥徹底有點無從下手了。你說讓我放碼,你卻遠而避之。數據庫
扯遠了,回到個人Training Project。若是光是完成這個Training Project,倒沒什麼。可是要求一個接一個。說一下印象中還算深入的編碼規範。編程
當時接受培訓的編碼規範是PHILIPS C# Coding Standard,沒據說過?那也沒什麼,微軟官網的一些Internal Coding Guidelines總要了解一下吧。咱們的代碼被Leader要求得用上PHILIPS的規範。我記得我很認真地在每一個接口和方法都加上註釋,該換行換行,該加括號加括號,變量命名也合乎英文語法(呵呵~你應該見過一半英文一半拼音的命名方式吧)。最後代碼Review的時候,Leader挑了一個讓我無語的地方:設計模式
/// <summary> /// Gets product info by product id. /// </summary> /// <param name="id">The product id</param> public Product GetProductById(long id);
「你看,全部的語句結束後面應該跟一個英文的「.」,你這個The product id後面沒有。」緩存
我想確定是個人代碼註釋寫得很規範,他挑不出其它的毛病來。你可能還看不出來的一點,Gets必須加s,呵呵。後來,我發現這樣的註釋簡直多餘,一看方法就知道幹嗎的好嗎。後來寫了點Objective-C,發現代碼基本不用寫什麼註釋,方法名就是一條短語,自我解釋。有的人還把方法名寫成一條句子,也是夠離譜的。安全
上面說了這麼多,什麼意思呢?知識儲備、動手能力很重要。代碼看過了也許你就忘了,敲過了纔算學過,能總結出來才叫理解。網絡
設計模式是從編碼的層面提煉出來的一種總結,而架構則着眼於全局,對系統的高層次抽象。因此,若是你尚未編寫過必定量的代碼(幾千行、幾萬行或十幾萬行,視我的而定),哪來的代碼重用、代碼可擴展?設計模式基本就是前人經驗的總結,有了必定的代碼基礎能更好地理解;架構則站在更高的維度,要求的就不僅僅是代碼經驗了,你還要懂硬件、操做系統、網絡環境等等,實踐和理論的結合。同時你還得了解技術的邊界,能作什麼,不能作什麼。
下面終於輪到MVC和MVP登場了,剛接觸這個概念的同窗可能會問:他們應該是一種設計模式吧。還真不是。那你上面還說了這麼多設計模式和架構?沒關係張,這不是對比學習嘛。這個問題理清還真是須要費點力氣,還好已經有人把這個問題搞明白了:爲何MVC不是一種設計模式。
從Android的角度看一下MVC:
Model
模型層就是一些基礎數據源,一般是數據庫SQLite、網絡請求的JSON、本地XML或Java對象數據。它表明了一些實體類,用來描述你的業務邏輯怎麼進行組合,同時也爲數據定義業務規則。
Controller
控制器是與應用程序相關聯的動做集合,它負責處理待響應的請求。它經過界面響應用戶輸入,經過模型層處理數據,最後返回結果給界面。控制器扮演着模型和界面的粘合劑角色。
View
界面就是各類UI組件(XML佈局或Java自定義控件對象)。它只負責展現數據,同時接收控制器傳過來的結果。
因此在Android中,activity界面就是View,本地數據或網絡數據就是Model,至於Controller嘛,看項目代碼怎麼組織了。通常來講,activity能夠認爲是Controller,一方面它負責視圖的呈現,一方面控制業務邏輯(先從本地取緩存數據,再從服務端刷新;等等)並處理相關數據。作得好一點,無非再封裝一層BusinessLogic,activity再去調用這個BusinessLogic,從而減輕activity的代碼負擔。但也逃離不了BusinessLogic+activity就是Controller的範疇,由於二者之間存在直接依賴,並且是依賴於具體實現。
上圖模擬了界面可能被用戶點擊,經過事件傳遞到控制器,接着控制器發起一個網絡請求,響應結果通過轉換到了模型層,最後控制器取得模型層的數據並通知界面進行刷新。Android用到MVC的具體實現不少,如ListView,Adapter就是典型的Controller,它在數據變化的時候,就是這樣通知界面的:
adapter.notifyDataSetChanged();
讓咱們簡化上面的圖示:
固然,這是一種理想狀態。在Android中,View和Model也有關聯的,因此更接近的圖示應該是這樣的:
MVP(Model-View-Presenter),你能夠把它看做MVC的一個變種,用來隔離UI、UI邏輯和業務邏輯、業務數據。
Presenter表明界面負責處理UI事件,它也須要經過界面來得到用戶的輸入,而後經過模型層處理數據,再返回結果給界面。跟View和Controller不一樣的地方在於:View和Presenter利用了接口機制,因此他們徹底解耦。因此MVP比MVC更利於後期的擴展和維護,是由於它針對了接口編程。看看上面的PetShop架構圖,是否是看到了衆多的Interface?瞭解我放那張圖的用心良苦了吧,面向接口編程和合理的層次劃分。看一個簡單的C#例子。
接口定義:
public interface IProduct { /// <summary> /// 獲取全部的產品信息 /// </summary> /// <returns>產品信息集合</returns> List<Product> GetAllProducts(); /// <summary> /// 經過產品編號獲取產品信息 /// </summary> /// <param name="productId">產品編號</param> /// <returns>產品實體具體信息</returns> Product GetProductById(long productId); }
從SQL Server數據庫獲取數據
public class SQLServerProvider : IProduct { public List<Product> GetAllProducts() { // TODO } public Product GetProductById(long productId) { // TODO } }
從Oracle數據獲取數據
public class OracleProvider : IProduct { public List<Product> GetAllProducts() { // TODO } public Product GetProductById(long productId) { // TODO } }
使用
class Program { static void Main(string[] args) { IProduct productProvider= new SQLServerProvider(); productProvider.getAllProducts(); // 或者 IProduct productProvider= new OraclerProvider(); productProvider.getAllProducts(); } }
只要接口不變,之後你想從SQLite、DB2獲取數據,只須要再寫個類實現IProduct接口就好了,徹底不須要修改原有的類,而後在實例化的時候換一下new的對象。是否是有點開閉的意味?對擴展開放,對修改關閉。這就是設計模式中的開閉原則(OCP:Open Closed Principle)。利用接口編程,也方便了後期進行單元測試。
回到咱們的Presenter,總結一下MVP的關鍵點:
有人實現了一個demo,咱們來學習一下吧 。如下是類圖:
四個接口:OnLoginFihishedListener, LoginPresenter, LoginInteractor, LoginView,兩個實現類:LoginPresenterImpl和LoginInteractorImpl,一個登陸界面LoginActivity。看起來有點費勁?我再把它抽象一下:
代碼核心以下,註釋中的1->2->3->4->5就是具體的調用流程
public class LoginActivity extends Activity implements LoginView, View.OnClickListener { private LoginPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ...... presenter = new LoginPresenterImpl(this); } @Override public void onClick(View v) { // 一、調用LoginPresenterImpl進行校驗 presenter.validateCredentials(username.getText().toString(), password.getText().toString()); } @Override public void navigateToHome() { // 五、回調結果,LoginActivity做跳轉 startActivity(new Intent(this, MainActivity.class)); finish(); } }
public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener { private LoginView loginView; private LoginInteractor loginInteractor; public LoginPresenterImpl(LoginView loginView) { this.loginView = loginView; this.loginInteractor = new LoginInteractorImpl(); } @Override public void validateCredentials(String username, String password) { // 回調,通知LoginActivity,顯示加載中提示 if (loginView != null) { loginView.showProgress(); } // 二、開始調用LoginInteractorImpl中的方法 loginInteractor.login(username, password, this); } @Override public void onSuccess() { // 四、回調,通知LoginActivity if (loginView != null) { loginView.navigateToHome(); } } }
public class LoginInteractorImpl implements LoginInteractor { @Override public void login(final String username, final String password, final OnLoginFinishedListener listener) { // 三、TODO,登陸邏輯。成功後回調給上層調用者 listener.onSuccess(); } }
好了,就這樣。畫圖太累了,如對你有幫助,就推薦一下吧。