1. 程式人生 > >iOS元件化方案的幾種實現

iOS元件化方案的幾種實現

最近研究了一下專案的元件化,把casabanglimboy的有關元件化的部落格看了一遍,學到了不少東西,對目前業界的元件化方案有了一定的瞭解。這些高質量的部落格大致討論了元件化的三種方案:url-blockprotocol-class(和url-controller類似)、target-action,以及應用這三種元件化方案的時機、步驟、利弊等等。

本文主要介紹一下這三種元件化方案的技術實現過程,針對不同元件化方案具體應用過程中可能出現的問題加以介紹,也針對casa批判蘑菇街的元件化方案加以自己的思考,希望對需要了解元件化的朋友有一定的幫助。

為什麼需要元件化

隨著公司業務的不斷髮展,專案的功能越來越複雜,各個業務程式碼耦合也越來越多,程式碼量也是急劇增加,傳統的MVC

或者MVVM架構已經無法高效的管理工程程式碼,因此需要用一種技術來更好地管理工程,而元件化是一種能夠解決程式碼耦合的技術。專案經過元件化的拆分,不僅可以解決程式碼耦合的問題,還可以增強程式碼的複用性,工程的易管理性等等。

元件化的過程

之前根據蘑菇街的元件化方案,limboycasa等人做了深入的討論,並根據各自的觀點給出了方案實施的理由以及利弊關係,然後又有人改進了他們的元件化方案,我總結了一下,大致有三種,下面分別介紹各自的實現過程:

方案一、url-block

這是蘑菇街中應用的一種頁面間呼叫的方式,通過在啟動時註冊元件提供的服務,把呼叫元件使用的url和元件提供的服務block

對應起來,儲存到記憶體中。在使用元件的服務時,通過url找到對應的block,然後獲取服務。

下圖是url-block的架構圖:

圖1

註冊:

[MGJRouter registerURLPattern:@"mgj://detail?id=:id" toHandler:^(NSDictionary *routerParameters) {
    NSNumber *id = routerParameters[@"id"];
    // create view controller with id
    // push view controller
}];

呼叫:

[MGJRouter openURL:@"mgj://detail?id=404"
]

蘑菇街為了統一iOSAndroid的平臺差異性,專門用後臺來管理url,然後針對不同的平臺,生成不同型別的檔案,來方便使用。

使用url-block的方案的確可以組建間的解耦,但是還是存在其它明顯的問題,比如:

  1. 需要在記憶體中維護url-block的表,元件多了可能會有記憶體問題
  2. url的引數傳遞受到限制,只能傳遞常規的字串引數,無法傳遞非常規引數,如UIImageNSData等型別
  3. 沒有區分本地呼叫和遠端呼叫的情況,尤其是遠端呼叫,會因為url引數受限,導致一些功能受限
  4. 元件本身依賴了中介軟體,且分散註冊使的耦合較多

方案二、protocol-class

針對方案一的問題,蘑菇街又提出了另一種元件化的方案,就是通過protocol定義服務介面,元件通過實現該介面來提供介面定義的服務,具體實現就是把protocolclass做一個對映,同時在記憶體中儲存一張對映表,使用的時候,就通過protocol找到對應的class來獲取需要的服務。

下圖是protocol-class的架構圖:

圖2

註冊:

[ModuleManager registerClass:ClassA forProtocol:ProtocolA]

呼叫:

[ModuleManager classForProtocol:ProtocolA]

蘑菇街的這種方案確實解決了方案一中無法傳遞非常規引數的問題,使得元件間的呼叫更為方便,但是它依然沒有解決元件依賴中介軟體的問題、記憶體中維護對映表的問題、元件的分散呼叫的問題。設計思想和方案一類似,都是通過給元件加了一層wrapper,然後給使用者呼叫。

同時,另一種方案是url-controller,這是LDBusMediator的元件化方案,我認為和方案二的實現原理類似。它是通過元件實現公共協議的服務,來對外提供服務。具體就是通過單例來維護url-controller的對映關係表,根據呼叫者的url,以及提供的引數(字典型別,所以引數型別不受約束)來返回對應的controller來提供服務;同時,為了增強元件提供服務的多樣性,又通過服務協議定義了其它的服務。整體來看,LDBusMediator解決了蘑菇街的這兩種元件化方案的不足,比如:通過註冊封裝件connector而不是block來降低了記憶體佔用;通過字典傳遞引數,解決了url引數的限制性。但是,由於使用了connector來提供服務而不是元件本身,把connector作為元件的一部分,依然有元件依賴中介軟體的問題。

圖3

方案三、target-action

casa的方案是通過給元件包裝一層wrapper來給外界提供服務,然後呼叫者通過依賴中介軟體來使用服務;其中,中介軟體是通過runtime來呼叫元件的服務,是真正意義上的解耦,也是該方案最核心的地方。具體實施過程是給元件封裝一層target物件來對外提供服務,不會對原來元件造成入侵;然後,通過實現中介軟體的category來提供服務給呼叫者,這樣使用者只需要依賴中介軟體,而元件則不需要依賴中介軟體。

下圖是casa的元件化方案架構圖:

圖4

以下程式碼來自casa的元件化demo

target

A元件

// TargetA.h

- (UIViewController *)Action_nativeFetchDetailViewController:(NSDictionary *)params;

CTMediator分類

// CTMediator+CTMediatorModuleAActions.h

- (UIViewController *)CTMediator_viewControllerForDetail;

// CTMediator+CTMediatorModuleAActions.m

- (UIViewController *)CTMediator_viewControllerForDetail
{
    return [self performTarget:kCTMediatorTargetA action:kCTMediatorActionNativFetchDetailViewController params:@{@"key":@"value"} shouldCacheTarget:NO];
}

呼叫

// ViewController.h
#import "CTMediator+CTMediatorModuleAActions.h"

[self presentViewController:[[CTMediator sharedInstance] CTMediator_viewControllerForDetail] animated:YES completion:nil];

從以上程式碼可以看出,使用者只需要依賴中介軟體,而中介軟體又不依賴元件,這是真正意義上的解耦。但是casa的這個方案有個問題就是hardcode,在中介軟體的category裡有hardcodecasa的解釋是在元件間呼叫時,最好是去model化,所以不可避免的引入了hardcode,並且所有的hardcode只存在於分類中。針對這個問題,有人提議,把所有的model做成元件化下沉,然後讓所有的元件都可以自由的訪問model,不過在我看來,這種方案雖然解決了元件間傳遞model的依賴問題,但是為了解決這個小問題,直接把整個model層元件化後暴露給所有元件,容易造成資料洩露,付出的代價有點大。針對這個問題,經過和網友討論,一致覺得元件間呼叫時用字典傳遞資料,元件內呼叫時用model傳遞資料,這樣即減少元件間資料對model的耦合,又方便了元件內使用model傳遞資料的便捷性。

元件化實施的方式

元件化可以利用git的原始碼管理工具的便利性來實施,具體就是建立一個專案工程的私有化倉庫,然後把各個元件的podspec上傳到私有倉庫,在需要用到元件時,直接從倉庫裡面取。

1.封裝公共庫和基礎UI庫

在具體的專案開發過程中,我們常會用到三方庫和自己封裝的UI庫,我們可以把這些庫封裝成元件,然後在專案裡用pod進行管理。其中,針對三方庫,最好再封裝一層,使我們的專案部直接依賴三方庫,方便後續開發過程中的更換。

2.獨立業務模組化

在開發過程中,對一些獨立的模組,如:登入模組、賬戶模組等等,也可以封裝成元件,因為這些元件是專案強依賴的,呼叫的頻次比較多。另外,在拆分元件化的過程中,拆分的粒度要合適,儘量做到元件的獨立性。同時,元件化是一個漸進的過程,不可能把一個完整的工程一下子全部元件化,要分步進行,通過不停的迭代,來最終實現專案的元件化。

3.服務介面最小化

在前兩步都完成的情況下,我們可以根據元件被呼叫的需求來抽象出元件對外的最小化介面。這時,就可以選擇具體應用哪種元件化方案來實施元件化了。

總結

元件化是專案架構層面的技術,不是所有專案都適合元件化,元件化一般針對的是大中型的專案,並且是多人開發。如果,專案比較小,開發人員比較少,確實不太適合元件化,因為這時的元件化可能帶來的不是便捷,而是增加了開發的工作量。另外,元件化過程也要考慮團隊的情況,總之,根據目前專案的情況作出最合適的技術選型。我一直尊崇,沒有最好的技術,只有最合適的技術。

實際上我的元件化經驗也不是很多,本文也是根據casalimboybang等的部落格中的內容,做了簡要的分析,針對文中的不足之處,還望大家指出,共同進步。(文中圖片來自網際網路,版權歸原作者所有)

參考資料