從.NET的寵物商店到Android MVC MVP

1 一些閒話

  記得剛進公司的時候,咱們除了作常規的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,發現代碼基本不用寫什麼註釋,方法名就是一條短語,自我解釋。有的人還把方法名寫成一條句子,也是夠離譜的。安全

  上面說了這麼多,什麼意思呢?知識儲備、動手能力很重要。代碼看過了也許你就忘了,敲過了纔算學過,能總結出來才叫理解。網絡

  • 設計模式是用來解決代碼重用的
  • 設計模式是用來隔離變化的
  • 架構是保證軟件的可用性、可擴展性、安全性

  設計模式是從編碼的層面提煉出來的一種總結,而架構則着眼於全局,對系統的高層次抽象。因此,若是你尚未編寫過必定量的代碼(幾千行、幾萬行或十幾萬行,視我的而定),哪來的代碼重用、代碼可擴展?設計模式基本就是前人經驗的總結,有了必定的代碼基礎能更好地理解;架構則站在更高的維度,要求的就不僅僅是代碼經驗了,你還要懂硬件、操做系統、網絡環境等等,實踐和理論的結合。同時你還得了解技術的邊界,能作什麼,不能作什麼。

2 MVC

  下面終於輪到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也有關聯的,因此更接近的圖示應該是這樣的:

3 MVP

  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的關鍵點:

  • 用戶與View進行交互
  • View和Presenter是一對一關係
  • View持有Presenter的引用,但View對Model沒有引用 

4 MVP在Android中的實現

  有人實現了一個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();
    }
}

  好了,就這樣。畫圖太累了,如對你有幫助,就推薦一下吧。

相關文章
相關標籤/搜索