MVVM

原文連接程序員

目前客戶端最流行的架構應該就是MVVM,然而在看了一些文章以後發現大部分是理論而並無仔細講解具體的架構方法和實踐,這篇博客說說我在實際工做中的使用。算法

引言

提到MVVM咱們不得不先來認識一下MVC: MVC模式(Model–view–controller)是軟件工程中的一種軟件架構模式,把軟件系統分爲三個基本部分:模型(Model)、視圖(View)和控制器(Controller)。MVC模式最先由Trygve Reenskaug在1978年提出[1],是施樂帕羅奧多研究中心(Xerox PARC)在20世紀80年代爲程序語言Smalltalk發明的一種軟件架構。MVC模式的目的是實現一種動態的程序設計,使後續對程序的修改和擴展簡化,而且使程序某一部分的重複利用成爲可能。除此以外,此模式經過對複雜度的簡化,使程序結構更加直觀。軟件系統經過對自身基本部分分離的同時也賦予了各個基本部分應有的功能。專業人員能夠經過自身的專長分組:數據庫

  • 控制器(Controller)- 負責轉發請求,對請求進行處理。
  • 視圖(View) - 界面設計人員進行圖形界面設計。
  • 模型(Model) - 程序員編寫程序應有的功能(實現算法等等)、數據庫專家進行數據管理和數據庫設計(能夠實現具體的功能)。

MVVM


MVVM是Model-View-ViewModel的簡寫,最先是由微軟公司提出並運用,是MVP(Model-View-Presenter)模式與WPF結合的應用方式時發展演變過來的一種新型架構架構。MVVM有助於將圖形用戶界面的開發與業務邏輯或後端邏輯(數據模型)的開發分離開來,這是經過置標語言或GUI代碼實現的。MVVM的視圖模型是一個值轉換器,這意味着視圖模型負責從模型中暴露(轉換)數據對象,以便輕鬆管理和呈現對象。在這方面,視圖模型比視圖作得更多,而且處理大部分視圖的顯示邏輯。視圖模型能夠實現中介者模式,組織對視圖所支持的用例集的後端邏輯的訪問。後端

  • 模型 模型是指表明真實狀態內容的領域模型(面向對象),或指表明內容的數據訪問層(以數據爲中心)。
  • 視圖 就像在MVC和MVP模式中同樣,視圖是用戶在屏幕上看到的結構、佈局和外觀(UI)。
  • 視圖模型 視圖模型是暴露公共屬性和命令的視圖的抽象。MVVM沒有MVC模式的控制器,也沒有MVP模式的presenter,有的是一個綁定器。在視圖模型中,綁定器在視圖和數據綁定器之間進行通訊。
  • 綁定器 聲明性數據和命令綁定隱含在MVVM模式中。在Microsoft解決方案堆中,綁定器是一種名爲XAML的標記語言。綁定器使開發人員免於被迫編寫樣板式邏輯來同步視圖模型和視圖。在微軟的堆以外實現時,聲明性數據綁定技術的出現是實現該模式的一個關鍵因素。

MVVM的優勢

解決controller過於臃腫


在MVC中很容易就會把一些業務邏輯,網絡請求,數據IO都放在controller中安全

注意,這裏不是說MVC的控制器必定很臃腫,而是「容易變得臃腫」網絡

在咱們新建一個工程的時候,蘋果會自動幫咱們生成一個ViewController,而在動手開始寫代碼的時候,每每控制不住就直接將邏輯寫在Controller中。架構

MVVM架構會要求咱們把任何與非View的邏輯玻璃出來,Controller中除了綁定viewModel以外的代碼只容許出現對View的操做。由於Controller對咱們來講也只是一個View。框架

邏輯分離


就像上面說的,業務邏輯都會抽離出來放在viewModel中 ,這樣能夠在任何地方重用這一堆業務數據庫設計

除此以外,咱們的代碼將會更加易於測試,避免出如今MVC中可能出現的那種超長的方法、嚴重依賴全局狀態致使難以測試的問題。mvvm

View重用 (可擁有view單獨的viewModel)


在MVVM中,View只須要與ViewMode交互,不會收到其餘的影響,因此不但vm、m 能夠重用,view同樣能夠重複使用,修改的時候也更加方便。

缺點


BUG與傳遞


因爲在MVVM裏面View和ViewModel是鬆耦合的,在測試出問題的時候就要排查各個地方的問題,

有多是vm中的也有多是view中的。因爲vm會傳遞數據,一個bug會很容易的傳遞到其餘地方,引起更大的問題。

而且其中一個地方出現問題的話,這個BUG就極有可能隨着傳遞到其餘的邏輯中,從而致使更嚴重的問題發生。

須要維護Model,viewModel,和view的開銷,controller至關於被抽象成View。


額外的viewModel使用也並非無代價的,有可能因爲各類緣由致使管理起來稍微複雜。而額外的,若是由於強引用或其餘緣由致使的循環引用等內存不能正確釋放的狀況下,有可能會內存瘋漲,因此須要確保你的使用方式是無反作用的。

使用方法


上面說了這些只是一個大體的介紹,咱們仍是來看看應該怎樣使用吧。

你可使用delegate的方式或者block的方式對view和viewModel進行橋接,在這裏咱們選擇使用delegate,我認爲這樣看着比較直觀,在代碼中也更加明確。

首先建立一個工程,選擇singleViewApplication,咱們就以最多見的的登錄功能做爲示例

新建用於由viewModel調用,view進行響應的protocol


首先要起個名字,就叫LoginViewModelDelegateProtocol 吧

@protocol LoginViewModelDelegateProtocol <NSObject>

@end
複製代碼

好,讓咱們想想view會發送一些什麼數據給VM ,VM都須要什麼數據。

對於簡單登錄的VM來講,咱們須要通知view的數據和方法

  • 登錄成功
  • 錯誤提示
  • 按鈕狀態改變(是否能夠點擊)

那麼咱們能夠在protocol中添加方法了

@protocol LoginViewModelDelegateProtocol <NSObject>

- (void)loginSuccess;
- (void)showTips:(NSString *)tip;
- (void)buttonEnable:(BOOL )enable;

@end
複製代碼

新建用於由view調用,viewModel進行響應的protocol


同理,咱們只要確認vm和v須要交換的數據就行了。

  • 用戶名輸入框的字符串
  • 密碼輸入框的字符串
  • 點擊登錄事件
@protocol LoginViewModelInterfaceProtocol <NSObject>

- (void)inputUserName:(NSString *)uname;
- (void)inputPwd:(NSString *)pwd;
- (void)didTapLoginBUtton;

@end
複製代碼

實現代理方法

下面咱們新建一個viewModel叫作LoginViewModel

LoginViewModel.h

#import <Foundation/Foundation.h>
#import "LoginViewModelDelegateProtocol.h"
#import "LoginViewModelInterfaceProtocol.h"

@interface LoginViewModel : NSObject<LoginViewModelInterfaceProtocol>

@property (nonatomic ,weak) id<LoginViewModelDelegateProtocol> delegate;

@end
複製代碼

LoginViewModel.m

#import "LoginViewModelDelegateProtocol.h"

@interface LoginViewModel ()

@property (assign, nonatomic) BOOL unameValid;

@property (assign, nonatomic) BOOL pwdValid;

@end

@implementation LoginViewModel

- (void)inputUserName:(NSString *)uname {
    self.unameValid = uname.length>0;
    [self judgeAllValid];
}
- (void)inputPwd:(NSString *)pwd {
    self.pwdValid = pwd.length>0;
    [self judgeAllValid];
}
- (void)didTapLoginBUtton {
    // 一些請求,這裏忽略網絡請求,直接模擬結果
    [self.delegate loginSuccess];
}

- (void)judgeAllValid {
    BOOL v = [self isAllValid];
    [self.delegate buttonEnable:v];
}

- (BOOL)isAllValid {
    return self.unameValid && self.pwdValid;
}

@end
複製代碼

而後在controller初始化,而且實現所有的方法就能夠了。

viewController.m

#import "viewController.h"
...

@interface viewController () <LoginViewModelDelegateProtocol>
@property (nonatomic ,strong) LoginViewModel *vm;
@end

@implementation TDFSetPhoneNumController

- (void)viewDidLoad {
    
    [super viewDidLoad];
    self.vm.delegate = self;
}

#pragma mark - VMDelegate

- (void)loginSuccess {
    [self.navigationController pushViewController:[SuccessVC new]] animated:true];
}
- (void)showTips:(NSString *)tip {
    [self showAlert:tip];
}
- (void)buttonEnable:(BOOL )enable {
    self.loginbutton.enable = enable;
}

#pragma mark - Getter

- (LoginViewModel *)vm {
    if (!_vm) {
        _vm = [LoginViewModel new];
    }
    return _vm;
}
@end
複製代碼

大功告成


這裏所有的代碼是我手寫的,後面省略了一些UIKit相關的佈局,想必以你的聰明才智應該已經能很輕鬆的將剩餘的補全了吧。

注意事項

這裏有幾點我認爲應該注意的:

  • vm與v之間不論經過什麼傳遞值和響應,都要保持數據的單向流動。
  • 代理用weak,內存回收時會自動置爲nil,
  • 所有model與viewModel中不該包含任何UIKit框架下的類

總結

總而言之,我認爲MVVM在咱們的代碼總體分工和應用架構的過程當中應用仍是十分優雅和安全的,

不過話說回來什麼架構也罷,仍是要看咱們怎麼去用它,不是嗎

相關文章
相關標籤/搜索