[愛上Swift]第14彈:在iOS項目中引入MVVM

來源:互聯網
上載者:User

標籤:blog   http   io   ar   os   使用   sp   for   strong   

轉自:http://www.cnblogs.com/sunshine-anycall/p/4153357.html

 

本文翻譯自:http://www.objc.io/issue-13/mvvm.html。為了方便讀者並節約時間,有些不是和文章主題相關的就去掉了。如果讀者要看原文的話可以通過前面的url直接存取。作者也是做了iOS多年,從大學一直到現在n多年了。對於開發一款有B格的APP很有追求。學習了很多的東西,比如,silver bullet什麼的,設計模式什麼的。但是,面對急速膨脹的代碼量,即使才高八鬥也顯得無所適從。於是他開始思考人生。。。

MVC?還有另外一個解釋:Massive View Controller,翻譯過來就是一大堆的View Controller的意思。有的時候真的時有這種感覺,View Controller太多了。尤其在一個人晚上加班改bug的時候,感覺更明顯。於是,你會恨不得全部推倒重來算了!

從架構的角度考慮,也許MVC的一個衍生架構MVVM更加的合適。這裡就不討論MVVM的前世今生了。園子裡的各位.NET達人從很久以前就已在WPF上玩這個東西了。先看一下iOS的MVC是什麼樣的,然後一步一步的進入MVVM。iOS的MVC:

這是一個典型的MVC架構。Models代表資料,views代表的時使用者介面,view controllers在中間協調。仔細一想你會發現,雖然view和view controller是兩個完全不同的東西,但是他們卻緊密結合。什麼時候一個view可以和不同的view controller結合使用,或者一個view controller可以和不同的view結合使用。所以,這個架構其實是這樣的:

這樣就跟準確的描述了MVC架構下的代碼是怎麼寫的。但是,這還沒有反應出來iOS開發中大量增加的view controller。在典型的MVC應用中,很多的邏輯處理放在view controller中。有些確實是需要放在view controller中的。但是還有很大一部分式“展示邏輯(presentation logic)”。這是一個MVVM的術語,指的是那些把Model裡d的資料轉換成view中可以顯示的形式的過程。比如,把一個NSData轉換成有一定格式的NSString就是這麼一個過程。

上面的圖都少了一部分內容。缺少了的就是那部分“展示邏輯”。這一部分抽象出來之後就可以叫做“view model”。這一部分在view、view controller和model之間。

看起來更合理了把。這附圖準確的描述了什麼是MVVM,一個增強版的MVC。這裡,我們可以正式的把view和controller串連起來。並把展示邏輯從view controller中挪出去,形成一個新的對象:view model。MVVM聽起來複雜,其實就是一個穿了件新衣的MVC。本質上和MVC是一樣的。

現在您應該清楚什麼是MVVM了。那麼,為什麼要用這個東西呢?因為,使用這個架構編寫代碼可以減少view controller的複雜度,並且展示的邏輯也更容易測試。下面就通過代碼展示一下MVVM的這兩點好處。

有三點一定在文中強調:

  1. MVVM和您現有的MVC架構是相容的。
  2. MVVM使您的APP更容易測試。
  3. MVVM可以和綁定型的架構很好共存。

如前文所述,MVVM就是一個加強版的MVC。所以很容易看到這個模式如何和之前的MVC架構共存。我們先建立一個簡單的Person model和對應的view controller。

import UIKitclass Person {    var salutation: String?    var firstName: String?    var lastName: String?    var birthDate: NSDate?        init(salutation: String?, firstName: String?, lastName: String?, birthDate: NSDate?){        self.salutation = salutation        self.firstName = firstName        self.lastName = lastName        self.birthDate = birthDate    }}

現在假設我們有一個view controller叫做PersonViewController,在viewDidLoad中要用model Person來設定一些Label的值:

override func viewDidLoad() {        super.viewDidLoad()                if self.model.salutation!.utf16Count > 0 {            self.nameLabel.text = "\(self.model.salutation) \(self.model.firstName) \(self.model.lastName)"        }        else{            self.nameLabel.text = "\(self.model.firstName) \(self.model.lastName)"        }                var dateFormatter = NSDateFormatter()        dateFormatter.dateFormat = "EEEE MMMM d, yyyy"        self.birthDateLabel.text = dateFormatter.stringFromDate(self.model.birthDate!)    }

這些都是最常規的MVC代碼的寫法。下面看看如何寫view model。

import UIKitclass PersonViewModel {    var person: Person!    var nameText: String?    var birthDateText: String?        init(person: Person){        self.person = person                if self.person?.salutation?.utf16Count > 0{            self.nameText = "\(self.person.salutation) \(self.person.firstName) \(self.person.lastName)"        }        else{            self.nameText = "\(self.person.firstName) \(self.person.lastName)"        }                var dateFormatter = NSDateFormatter()        dateFormatter.dateFormat = "EEEE MMMM d, yyyy"        self.birthDateText = dateFormatter.stringFromDate(self.person.birthDate!)    }}

這樣,顯示邏輯已經從viewDidLoad方法中遷移了出去。現在viewDidLoad方法就顯得輕量了很多。

override func viewDidLoad() {        super.viewDidLoad()                self.nameLabel.text = self.viewModel.nameText        self.birthDateLabel.text = self.viewModel.birthDateText    }

你會看到,並不需要對MVC架構做多大的修改。幾乎只是原來的代碼稍作移動。完全和MVC相容,減少了view controller的重量,而且變得更加容易測試。

下面說說可測試。view controller的難以測試簡直是出了名的。在MVVM中,代碼很大一部分都移到了view model中。並且view model都是很容易測試的。如:

var person: Person!        override func setUp() {        super.setUp()        var salutation = "Dr."        var firstName = "first"        var lastName = "last"        var birthDate = NSDate(timeIntervalSince1970: 0)        self.person = Person(salutation: salutation, firstName: firstName, lastName: lastName, birthDate: birthDate)    }    func testUserSalutation(){        var viewModel = PersonViewModel(person: self.person)        XCTAssert(viewModel.nameText! == "Dr. first last" , "use salutation available \(viewModel.nameText!)")    }    func testNoSalutation(){        var localPerson = Person(salutation: nil, firstName: "first", lastName: "last", birthDate: NSDate(timeIntervalSince1970: 0))        var viewModel = PersonViewModel(person: localPerson)        XCTAssert(viewModel.nameText! == "first last", "should not use salutation \(viewModel.nameText!)")    }        func testBirthDateFormat(){        var viewModel = PersonViewModel(person: self.person)        XCTAssert(viewModel.birthDateText! == "Thursday January 1, 1970", "date \(viewModel.birthDateText!)")    }

如果不是把代碼移到了view model中,測試的時候就不得不初始化一個view controller,當然還有view controller裡的一堆view。然後對比的時這些view(上例提到的時Label)的某些屬性的值。這樣不僅是非常的不方便、不直接,而且還會形成一種非常脆弱的測試:因為,view controller的試圖層次根本就不能有任何的變動,一旦變動測試即告失敗!或者測試根本就編譯不過。使用MVVM對於測試的益處是顯而易見的。在上例中還是比較簡單的邏輯。如果換做其他的產品環境的複雜的顯示邏輯的話,這種易於測試的好處會更加明顯。

注意到,上面使用到的例子都是比較簡單的。沒什麼需要修改的屬性。可以直接使用初始值初始化對象。對於有修改的model。我們需要用到某中綁定機制。這樣在model的某些值修改以後才能保證基於這個model的view model的值也跟著改變。更深一步,model值修改之後,view調用的view model的值也能跟著修改。也就是,一個對於model的修改,和model相關的view model和view的值都能同步的更新。

在OSX上,可以使用Cocoa bindings。但是iOS上沒有這個奢侈品。但是key-value observation完全可以勝任。只是在很多屬性的時候會增加代碼量。也可以使用ReactiveCocoa。當然這隻是一個選擇。一個不錯的綁定架構,會使MVVM好上加好。

我們已經講了很多關於MVVM的內容。從易於測試的角度講了MVVM。一個好的綁定架構會使MVVM更加好用。如果要更多的瞭解MVVM的話可以看這裡。這些文章更多的講述了MVVM的好處。或者這一篇,講述了在原有項目上使用MVVM並取得了不錯的效果。還有這裡的一個開源app,完全是基於MVVM的,讀者可以參考。

[愛上Swift]第14彈:在iOS項目中引入MVVM

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.