亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

瀏覽:3日期:2022-09-17 11:15:30
引言

58 同城的搜索功能支撐了近一半的用戶流量,所以搜索是一個(gè)很重要的模塊。眾所周知,iPhone 的搜索是通過 Spotlight 來實(shí)現(xiàn)的,那么在 App 內(nèi)部是如何實(shí)現(xiàn)搜索呢?首先了解一下 58 同城的搜索需求:

58 同城首頁(yè),提供搜索功能,稱為全站搜。 58 同城有二手物品、房產(chǎn)、二手車、招聘、黃頁(yè)幾大業(yè)務(wù)線,這是粗粒度的業(yè)務(wù)線。細(xì)分一下,二手可以拆分出二手物品、寵物等類別;房產(chǎn)拆分出租房、二手房等類別;招聘拆分出全職招聘、兼職等類別;黃頁(yè)拆分出家政、本地服務(wù)等類別。拆分出的這些較細(xì)的類別的頁(yè)面稱之為大類頁(yè),這些大類頁(yè)也提供搜索功能,稱為大類搜。 大類頁(yè)提供更細(xì)粒度的類別,如進(jìn)入二手房大類頁(yè)后會(huì)看到二手房、新房、商鋪、廠房等入口,再次進(jìn)入后是列表頁(yè),這些列表頁(yè)也提供搜索功能,稱為列表搜。

圖 1 是舊的搜索框架,雖然看上去比較清晰,但實(shí)際上存在著很多問題,比如代碼冗余、耦合度高、不易復(fù)用等。這些也是一些大型模塊經(jīng)過多次升級(jí),到了后期經(jīng)常存在的問題。接下來具體問題具體分析。

存在的問題

從最開始的 1.0 版本,就在首頁(yè)實(shí)現(xiàn)了搜索功能。隨著業(yè)務(wù)的擴(kuò)展,58 的業(yè)務(wù)線也在逐漸成型和完善,每個(gè)業(yè)務(wù)線的大類頁(yè)接入搜索功能也存在先后。業(yè)務(wù)線內(nèi)部的列表頁(yè),更是多種多樣,比如 Native 的類別頁(yè)、Web 列表頁(yè),還有特殊的列表頁(yè)(如簡(jiǎn)歷庫(kù)列表頁(yè)、地圖搜房頁(yè)等),這些列表頁(yè)后來也都一一實(shí)現(xiàn)了搜索功能。不過也正是因?yàn)閷?shí)現(xiàn)的時(shí)間有先后,逐漸積累產(chǎn)生了一些歷史遺留問題。

代碼冗余

不同的業(yè)務(wù)頁(yè)面對(duì)搜索功能的支持有先后之別,后實(shí)現(xiàn)搜索的頁(yè)面都是先拷貝一份先實(shí)現(xiàn)搜索的代碼,然后把其中的業(yè)務(wù)代碼刪除,加入自己的。舊版的業(yè)務(wù)入口頁(yè)面各自實(shí)現(xiàn)了一套搜索邏輯(如圖 2 所示),重復(fù)的代碼超過一萬行。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 2 業(yè)務(wù)頁(yè)面各自實(shí)現(xiàn)搜索頁(yè)面

耦合度高

從圖 1 可以看出,搜索頁(yè)面是由業(yè)務(wù)入口頁(yè)面管理和加載的。搜索頁(yè)面要處理數(shù)據(jù),包括熱詞和搜索歷史的本地獲取、服務(wù)器獲取;要處理網(wǎng)絡(luò)請(qǐng)求,包括關(guān)鍵詞的聯(lián)想請(qǐng)求、搜索請(qǐng)求;還要實(shí)現(xiàn)視圖的協(xié)議方法。這些大量的邏輯都是在業(yè)務(wù)頁(yè)面文件中實(shí)現(xiàn)的。

在代碼管理上,盡管已經(jīng)把搜索相關(guān)的代碼剝離出來,單獨(dú)放到了一個(gè) Category 類別文件中,但實(shí)際上還是無法避免地跟業(yè)務(wù)頁(yè)面邏輯耦合在一起。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 1 舊版搜索框架

文件級(jí)別。業(yè)務(wù)頁(yè)面文件中,既有業(yè)務(wù)方法,又有搜索相關(guān)的方法; 方法級(jí)別。在同一個(gè)方法中,既有業(yè)務(wù)邏輯,又有搜索邏輯。這是更為嚴(yán)重的耦合。 搜索頁(yè)面無法復(fù)用

因?yàn)樗阉黜?yè)面與業(yè)務(wù)頁(yè)面耦合度高,所以業(yè)務(wù)入口頁(yè)面無法復(fù)用以前的搜索頁(yè)面,只能各自實(shí)現(xiàn)。相反的,越來越多的業(yè)務(wù)入口頁(yè)面不再考慮搜索頁(yè)面的復(fù)用性,只考慮自己獨(dú)自實(shí)現(xiàn),導(dǎo)致搜索頁(yè)面越來越多,比以前更加難以復(fù)用和移植。

另一個(gè)無法復(fù)用的原因來自于搜索頁(yè)面自身的定制化嚴(yán)重。搜索頁(yè)面需要清楚地知道是否存在熱詞、搜索歷史、聯(lián)想結(jié)果等,然后定制顯示視圖,如圖 3 所示是兩個(gè)搜索頁(yè)面的簡(jiǎn)化類圖。搜索頁(yè)面的列表協(xié)議方法均直接訪問了搜索頁(yè)面的屬性,但其屬性是與搜索業(yè)務(wù)直接相關(guān)的。全站搜中沒有城市業(yè)務(wù),而城市選擇頁(yè)搜頁(yè)面也沒有熱詞等業(yè)務(wù),這樣的定制導(dǎo)致搜索頁(yè)面無法復(fù)用。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 3 兩個(gè)搜索頁(yè)面的簡(jiǎn)化類圖

業(yè)務(wù)功能接入成本高

業(yè)務(wù)功能接入成本高,表現(xiàn)在以下兩個(gè)方面:

代碼復(fù)用度低,開發(fā)成本高:業(yè)務(wù)頁(yè)面想實(shí)現(xiàn)搜索頁(yè)面成本高。前面已經(jīng)介紹過了,搜索頁(yè)面無法復(fù)用,業(yè)務(wù)頁(yè)面需要自行實(shí)現(xiàn)一套搜索頁(yè)面。 搜索頁(yè)面與搜索結(jié)果頁(yè)耦合性高:從搜索頁(yè)面到搜索結(jié)果頁(yè)之間的跳轉(zhuǎn)只處理了固定頁(yè)面的跳轉(zhuǎn)。如果是列表頁(yè)面接入搜索頁(yè)面,只需要基于當(dāng)前頁(yè)面刷新即可。而全站搜和大類搜是最靈活的,根據(jù)搜索詞可以跳轉(zhuǎn)到所有業(yè)務(wù)線的落地頁(yè)。但是 JumpManager 模塊只處理了固定的頁(yè)面跳轉(zhuǎn)(如圖 1,只有搜索類別頁(yè)、搜索結(jié)果列表頁(yè)),假如業(yè)務(wù)線想搜索后跳轉(zhuǎn)到一個(gè)自定義的頁(yè)面(如搜索“拼車”),JumpManager 是無法實(shí)現(xiàn)的。

這種只能跳轉(zhuǎn)到固定、有限頁(yè)面的跳轉(zhuǎn)方式,限制了快速變化的業(yè)務(wù)需求。如果業(yè)務(wù)功能要接入一個(gè)新的跳轉(zhuǎn)目標(biāo)頁(yè)面,需要發(fā)版才能實(shí)現(xiàn),成本很高。

新搜索框架設(shè)計(jì)

針對(duì)上面的歷史遺留問題,重新設(shè)計(jì)了搜索框架:

把搜索頁(yè)面從業(yè)務(wù)入口頁(yè)面中解耦出來,降低了耦合度; 實(shí)現(xiàn)了路由中心,讓 App 內(nèi)頁(yè)面的跳轉(zhuǎn)變得簡(jiǎn)單,降低了業(yè)務(wù)線接入成本; 實(shí)現(xiàn)了搜索頁(yè)面的組件化,提高了搜索頁(yè)面的復(fù)用性,減少了代碼冗余。

如圖 4 所示是新搜索框架圖。整體來說是從三個(gè)層級(jí)實(shí)現(xiàn)了搜索模塊的組件化。最外層,通過路由中心,業(yè)務(wù)入口可以跳轉(zhuǎn)到搜索頁(yè)面,搜索頁(yè)面可以跳轉(zhuǎn)到更多的結(jié)果頁(yè)面;中間層,搜索頁(yè)面內(nèi)部可以配置搜索框組件和語音組件;最內(nèi)層是 UITableView 內(nèi)部的組件化實(shí)現(xiàn),列表中的元素根據(jù)數(shù)據(jù)可以靈活、動(dòng)態(tài)地展示任意組合的樣式,消去了邏輯判斷和業(yè)務(wù)依賴。下面詳細(xì)展開說明新框架。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 4 新版搜索框架

搜索入口解耦

以前的搜索頁(yè)面是在業(yè)務(wù)頁(yè)面的視圖控制器內(nèi)加載的,搜索頁(yè)面是一個(gè) UIView,是業(yè)務(wù)頁(yè)面的一個(gè)子視圖。業(yè)務(wù)頁(yè)面除了處理自身業(yè)務(wù)邏輯,還需要實(shí)現(xiàn)搜索頁(yè)面的大量邏輯,導(dǎo)致視圖控制器動(dòng)輒幾千行,難以維護(hù),代碼可讀性較差,視圖控制器邏輯太多、過于復(fù)雜。

新框架中,對(duì)搜索入口進(jìn)行了解耦,方法是使用假搜輸入框作為搜索入口,如圖 5 所示的視圖層級(jí),用戶能看到搜索框,也可以點(diǎn)擊搜索框,但是無法輸入字符,因?yàn)樗阉骺蛏厦娓采w了一層透明的 UIButton 按鈕。當(dāng)用戶點(diǎn)擊輸入框時(shí),其實(shí)是觸發(fā)了 UIButton 的 Target 方法,然后通過路由中心跳轉(zhuǎn)到搜索頁(yè)面。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 5 搜索入口解耦方式

搜索頁(yè)面解耦

以前的搜索頁(yè)面,數(shù)據(jù)和 UI 顯示是耦合在一起的。UI 頁(yè)面直接訪問數(shù)據(jù)屬性,因?yàn)樾枰私怙@示的是什么數(shù)據(jù)模型、數(shù)據(jù)模型的 Class。其他業(yè)務(wù)頁(yè)面很難去復(fù)用這樣的搜索頁(yè)面。

新搜索框架中,把搜索頁(yè)面劃分為兩層,上層是數(shù)據(jù)層,是真正與業(yè)務(wù)有關(guān)的子模塊;下層是組件層,與業(yè)務(wù)無關(guān),是可以復(fù)用的模塊。

數(shù)據(jù)層。每個(gè)業(yè)務(wù)頁(yè)面需要的搜索功能是不用的,區(qū)別在于 UI 展示的數(shù)據(jù)不相同。數(shù)據(jù)層處理與數(shù)據(jù)有關(guān)的搜索業(yè)務(wù)邏輯,如數(shù)據(jù)的獲取、緩存、網(wǎng)絡(luò)請(qǐng)求等,除此之外還需要把數(shù)據(jù)組裝到一個(gè)數(shù)組中。 組件層。搜索頁(yè)面的組件化分為兩層,外層是搜索框組件、語音組件的可配置化,內(nèi)層是 UITableView 內(nèi)部的組件化。組件化模塊不需要了解數(shù)據(jù)的細(xì)節(jié),只需要拿到數(shù)據(jù)層傳來的數(shù)據(jù),然后轉(zhuǎn)化成組件來顯示。

圖 6 是全站搜和城市選擇頁(yè)面的搜索頁(yè)面,兩個(gè)頁(yè)面的數(shù)據(jù)層處理各自的搜索邏輯,但是公用組件層,當(dāng)組件顯示到列表中的時(shí)候,列表不需要知道 Cell 的具體類型,只當(dāng)作基類類型即可。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 6 搜索頁(yè)面通過組件化解耦

路由中心

舊的搜索框架,搜索完成后的跳轉(zhuǎn)通過 JumpManager 實(shí)現(xiàn),且只能跳轉(zhuǎn)到有限的兩種頁(yè)面。雖然頁(yè)面可以根據(jù)接口展現(xiàn)不同內(nèi)容,但隨著業(yè)務(wù)發(fā)展,舊搜索框架已無法滿足。

比如,隨著 iOS 系統(tǒng)的升級(jí),App 開始支持 WKWebView,但是因?yàn)槭褂?UIWebView 頁(yè)面的業(yè)務(wù)線還很多,所以需要長(zhǎng)時(shí)間保持兩者共存的方式。搜索關(guān)鍵詞進(jìn)入 Web 類型的結(jié)果頁(yè)面時(shí),如何控制 App 進(jìn)入的是 WKWebView 類型的頁(yè)面還是 UIWebView 類型的頁(yè)面?

最初可能通過判斷參數(shù)來實(shí)現(xiàn)。Server 返回的結(jié)果中有一個(gè)參數(shù) webType,當(dāng)值為 WKWebView 時(shí)采用 WKWebView 類型,否則采用 UIWebView 類型。但是當(dāng)隨著業(yè)務(wù)的增長(zhǎng),將會(huì)充斥著大量 if/else 的代碼,各種問題接踵而至。

最終我們決定采用更加靈活的方式,即通過路由中心來實(shí)現(xiàn)。路由中心不但解決了搜索框架中頁(yè)面跳轉(zhuǎn)的難題,其他業(yè)務(wù)需求在頁(yè)面跳轉(zhuǎn)時(shí)遇到的問題也得以解決。

設(shè)計(jì)目標(biāo)

使用簡(jiǎn)單。路由中心對(duì)使用方而言是一個(gè)黑盒子,使用方不需要關(guān)心如何實(shí)現(xiàn);

支持多種應(yīng)用場(chǎng)景。以圖 7 中的 5 種場(chǎng)景,路由中心都可以處理,而且可以跳轉(zhuǎn)到右側(cè)的四種頁(yè)面中的任意一個(gè)頁(yè)面;

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 7 路由中心設(shè)計(jì)目標(biāo)

穩(wěn)定、不輕易變化。因?yàn)樾枰С?App 中所有頁(yè)面的跳轉(zhuǎn),如果路由中心不穩(wěn)定,將會(huì)造成很嚴(yán)重的后果;

擴(kuò)展簡(jiǎn)單,易維護(hù)。業(yè)務(wù)發(fā)展很快,增加頁(yè)面是常見的事,需要路由中心很容易實(shí)現(xiàn)對(duì)新頁(yè)面的擴(kuò)展。

如圖 8 所示即是路由中心的設(shè)計(jì)圖。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 8 路由中心框架

路由入口

提供了兩類路由入口方法,一類是針對(duì)新數(shù)據(jù)協(xié)議的 Scheme URL 路徑類型的參數(shù),另一類是針對(duì)舊數(shù)據(jù)協(xié)議的參數(shù),如 Server 傳送來的數(shù)據(jù)中還有很多在使用字典類型舊協(xié)議數(shù)據(jù)。

協(xié)議數(shù)據(jù)轉(zhuǎn)換層

不同的路由入口,傳入的數(shù)據(jù)格式不一樣,如果不進(jìn)行統(tǒng)一,后面的處理將會(huì)有兩套邏輯,出現(xiàn)多個(gè) if/else 的分支代碼,代碼冗余、升級(jí)維護(hù)麻煩。根據(jù)當(dāng)前的使用頻率和未來的發(fā)展方向,最終選擇是把舊協(xié)議數(shù)據(jù)轉(zhuǎn)換成新協(xié)議數(shù)據(jù)。

新協(xié)議規(guī)則

新舊協(xié)議中包含的字段是一樣的,只是舊的跳轉(zhuǎn)協(xié)議中,參數(shù)被放在了字典中,而新跳轉(zhuǎn)協(xié)議中,參數(shù)是在一個(gè)長(zhǎng)字符串中。為了把舊的跳轉(zhuǎn)協(xié)議轉(zhuǎn)換成新協(xié)議數(shù)據(jù),制定了新跳轉(zhuǎn)協(xié)議規(guī)則格式:

其中:

wbscheme:是 scheme,用于協(xié)議區(qū)分。外部調(diào)起時(shí),區(qū)分是不是 58 的交互協(xié)議; router:authority,用于業(yè)務(wù)區(qū)分。區(qū)分是不是跳起協(xié)議; pagetype:屬于 URL 的 Path,用于區(qū)分頁(yè)面類別,如首頁(yè)、列表頁(yè)、詳情頁(yè)等; tradeline:屬于 URL 的 Path,用于區(qū)分業(yè)務(wù)線,如二手業(yè)務(wù)線,房產(chǎn)業(yè)務(wù)線,招聘業(yè)務(wù)線等等; otherparams=JsonString:query 參數(shù),表示跳轉(zhuǎn)時(shí)需要攜帶的參數(shù); 可以擴(kuò)展其他字端,以解決跳轉(zhuǎn)時(shí)頁(yè)面關(guān)閉、是否登錄等問題。

制定了協(xié)議規(guī)則之后,就可以把舊協(xié)議數(shù)據(jù)與新協(xié)議字段相對(duì)應(yīng)了。數(shù)據(jù)轉(zhuǎn)換工作是由轉(zhuǎn)換器完成的。

業(yè)務(wù)線分發(fā)轉(zhuǎn)換器

前面已經(jīng)介紹了新舊跳轉(zhuǎn)協(xié)議的參數(shù)對(duì)應(yīng)關(guān)系,現(xiàn)在需要一個(gè)轉(zhuǎn)換器把舊協(xié)議數(shù)據(jù)轉(zhuǎn)換成新跳轉(zhuǎn)協(xié)議。但是因?yàn)楦鳂I(yè)務(wù)線、主 App 主業(yè)務(wù)、其他創(chuàng)新業(yè)務(wù)等在跳轉(zhuǎn)時(shí),Server 傳入的參數(shù)、需要的參數(shù)可能都不一樣,所以需要多個(gè)轉(zhuǎn)換器,根據(jù)業(yè)務(wù)線(trandline 參數(shù)),我們制定了如圖 9 所示的多個(gè)轉(zhuǎn)換器。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 9 業(yè)務(wù)線分發(fā)轉(zhuǎn)換器流程

每一個(gè)轉(zhuǎn)換器都是一個(gè)單例,并且都實(shí)現(xiàn)一個(gè) -(NSString *)dispatchActionData:(NSDictionary *)aJsonDic 方法,作用是把字典類型的舊協(xié)議數(shù)據(jù)轉(zhuǎn)換為字符串類型的新協(xié)議數(shù)據(jù)。

根據(jù) tradeline 參數(shù),就可以在轉(zhuǎn)換器映射表中得到其 ClassName,然后通過轉(zhuǎn)換器調(diào)用 dispatchAction:方法,即可完成轉(zhuǎn)換。

注冊(cè)表

前面已經(jīng)看到了跳轉(zhuǎn)協(xié)議的數(shù)據(jù)格式,其中兩個(gè)參數(shù)最重要:tradeline 和 pagetype,因?yàn)樾枰獌蓚€(gè)參數(shù)來定位一個(gè)視圖控制器。

在同一個(gè)業(yè)務(wù)線的注冊(cè)表中,key 與視圖控制器唯一對(duì)應(yīng)。不同的業(yè)務(wù)線,key 值可以重復(fù)。圖 10 分別是黃頁(yè)業(yè)務(wù)線和二手車業(yè)務(wù)線的注冊(cè)表,其中 key 表示 pagetype,value 是視圖控制器的 Class Name。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 10 注冊(cè)表文件內(nèi)容

跳轉(zhuǎn)管理

解析跳轉(zhuǎn)協(xié)議

前面已經(jīng)統(tǒng)一了跳轉(zhuǎn)協(xié)議,所以在這里只需要解析一種格式的數(shù)據(jù)即可。解析后可以得到 tradeline 參數(shù)、pagetype 參數(shù)以及 param 字典。

目標(biāo)頁(yè)面管理

通過注冊(cè)表可以基于 tradeline 參數(shù)和 pagetype 參數(shù)來定位一個(gè)唯一的視圖控制器。所以在解析完跳轉(zhuǎn)協(xié)議后,可以利用得到的 Class Name 通過運(yùn)行時(shí)方法創(chuàng)建目標(biāo)頁(yè)面,最后把 param 參數(shù)傳遞給目標(biāo)頁(yè)面。

跳轉(zhuǎn)控制

跳轉(zhuǎn)控制可以通過 Native 代碼或者 Server 來控制,如果未指定,則采用簡(jiǎn)單的 Push 方式跳轉(zhuǎn)。路由中心支持以下 6 種方式:

簡(jiǎn)單的 Push 跳轉(zhuǎn)到目標(biāo)頁(yè): 跳轉(zhuǎn)到目標(biāo)頁(yè)之前 pop 到上級(jí)頁(yè)面; 跳轉(zhuǎn)到目標(biāo)頁(yè)之前 PopToRootViewController; 通過 Present 的方式呈現(xiàn)目標(biāo)頁(yè); 跳轉(zhuǎn)前先登錄,登錄成功之后才能跳轉(zhuǎn); 跳轉(zhuǎn)過程中沒有動(dòng)畫。

這 6 種方式的跳轉(zhuǎn)都可以在跳轉(zhuǎn)協(xié)議中增加參數(shù)來控制,實(shí)現(xiàn)了跳轉(zhuǎn)方式的多樣性和靈活性。

視圖控制器

視圖控制器協(xié)議

在通過 Class Name 創(chuàng)建對(duì)象后,為了更統(tǒng)一地創(chuàng)建視圖控制器、給視圖控制器對(duì)象賦值,聲明了一個(gè)協(xié)議。協(xié)議只有一個(gè)方法,注冊(cè)表中的所有視圖控制器都需要實(shí)現(xiàn)這個(gè)方法。

視圖控制器擴(kuò)展

通過注冊(cè)表可以通過 tradeline 參數(shù)和 pagetype 參數(shù)來定位一個(gè)唯一的視圖控制器。也就是說所有的目標(biāo)頁(yè)面都有屬于自己的 tradeline 參數(shù)、pagetype 參數(shù)。無法給所有的目標(biāo)頁(yè)面增加這兩個(gè)屬性,也不能讓所有的目標(biāo)頁(yè)面擁有同一個(gè)基類,更好的方式是增加一個(gè) UIViewController 的擴(kuò)展。

擴(kuò)展中除了動(dòng)態(tài)增加 tradeline、pagetype 兩個(gè)屬性外,還加入了跳轉(zhuǎn)控制的中提及的一些參數(shù)作為屬性,方便跳轉(zhuǎn)控制。

搜索頁(yè)面組件化

下面分別說明搜索頁(yè)面內(nèi)部組件化的兩層。

搜索框組件、語音組件可配置

搜索頁(yè)面中沒有使用系統(tǒng)的導(dǎo)航欄,是為了更好地定制導(dǎo)航欄位置的搜索框視圖。搜索頁(yè)面把搜索框視圖組裝成一個(gè)組件,可以根據(jù)需求進(jìn)行靈活配置。比如 58 同城 App 的 7.12.0 版本,全站搜的搜索框增加了一個(gè)前置類別選擇框,這是一個(gè)新的搜索框組件,只需要在全站搜搜索頁(yè)面替換搜索框組件即可,而不需要修改搜索頁(yè)面的其他代碼,影響最小。

語音組件也是可以配置的,如果某些業(yè)務(wù)頁(yè)面的搜索頁(yè)面不需要,則可以不顯示該組件。

這兩個(gè)組件的配置都是通過數(shù)據(jù)層根據(jù)業(yè)務(wù)來實(shí)現(xiàn)的。

下面著重說明 UITableView 內(nèi)部的組件化。

數(shù)據(jù)模型適配器

數(shù)據(jù)模型適配器的作用是把原始數(shù)據(jù)轉(zhuǎn)換成與業(yè)務(wù)有關(guān)的數(shù)據(jù)模型。原始數(shù)據(jù)可能來自于 Server,也可能是來自于本地緩存。原始數(shù)據(jù)一般不外乎 NSDictionary、NSArray,為了更好地操作數(shù)據(jù),需要轉(zhuǎn)換成已有的數(shù)據(jù)模型。

在搜索頁(yè)面實(shí)現(xiàn)組件化,數(shù)據(jù)就是 Native 拼接的。比如在全站搜、大類搜中,就是把熱詞、搜索歷史拼接為一個(gè)數(shù)組,然后交給數(shù)據(jù)模型工廠處理。數(shù)據(jù)模型工廠解析原始數(shù)據(jù),然后輸出數(shù)據(jù) model 數(shù)組。

如圖 11 是全站搜的數(shù)據(jù)模型適配器的輸入和輸出,輸入的是熱詞和搜索歷史數(shù)據(jù),而經(jīng)適配器轉(zhuǎn)換之后,數(shù)據(jù)被轉(zhuǎn)換為 {key : model} 字典組成的數(shù)組。保留 key 值,是為了使用 key 經(jīng)過視圖模型注冊(cè)表得到對(duì)應(yīng) cell 的 Class Name,而 model 對(duì)象則是用來為 cell 賦值的。下面詳細(xì)解析說明。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 11 數(shù)據(jù)模型適配器的輸入和輸出

原始數(shù)據(jù)

傳入的原始數(shù)據(jù)是一個(gè)數(shù)組,數(shù)組中的元素可能是數(shù)組,也可能是字典,如圖 12 所示,左側(cè)原始數(shù)組,都是字典形式,有 10 個(gè)字典,那么最后展現(xiàn)在 UITableView 中,section 數(shù)目為 1,row 數(shù)目為 10;而右側(cè)原始數(shù)組中又包含了兩個(gè)數(shù)組,說明 UITableView 中會(huì)有 2 個(gè) section。其中第一個(gè)子數(shù)組只有 1 個(gè)元素,第二個(gè)子數(shù)組有 4 個(gè)元素,說明 UITableView 的兩個(gè) section 分別會(huì)有 1 個(gè)和 4 個(gè) row。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 12 兩個(gè)原始數(shù)據(jù)

原始數(shù)組中,還有一個(gè)很重要的參數(shù)是字典的 key 值,比如右側(cè)原始數(shù)組中有一個(gè) key:hotword,在數(shù)據(jù)模型映射表(如圖 13 所示)中會(huì)存在對(duì)應(yīng)這個(gè) key 的一個(gè)數(shù)據(jù) model:WBSearchHotWordModel。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 13 數(shù)據(jù)模型注冊(cè)表

數(shù)據(jù)模型注冊(cè)表

數(shù)據(jù)模型注冊(cè)表是一個(gè)字典,在解析原始數(shù)據(jù)中,通過 key 值可以找到對(duì)應(yīng)的數(shù)據(jù) model 的 Class Name,圖 13 是搜索模塊的數(shù)據(jù)模型注冊(cè)表。

數(shù)據(jù)模型池

數(shù)據(jù)模型池不是一個(gè)類、文件、模塊,而是多個(gè)數(shù)據(jù) model 的集合。數(shù)據(jù)模型池至少包含了數(shù)據(jù)模型映射表中所需要的數(shù)據(jù)模型。

數(shù)據(jù)模型除了聲明一些業(yè)務(wù)需要的屬性之外,還需要實(shí)現(xiàn)一個(gè)解析方法 - (void)generateModelWithOriginalDict:(NSDictionary *)dict。

數(shù)據(jù)模型轉(zhuǎn)換

數(shù)據(jù)模型轉(zhuǎn)換過程如下:

遍歷原始數(shù)據(jù)數(shù)組,取出字典中的 key 值; 通過數(shù)據(jù)模型映射表,找到 key 對(duì)應(yīng)的 Class Name; 創(chuàng)建數(shù)據(jù)模型 Class Name 的實(shí)例對(duì)象 model; 利用數(shù)據(jù)模型實(shí)現(xiàn)的 generateModelWithOriginalDict:傳入數(shù)據(jù); 把{key : model}這個(gè)字段作為元素加入到新的結(jié)果數(shù)組中; 遍歷完成,返回新的結(jié)果數(shù)組。

視圖模型適配器

數(shù)據(jù)是為視圖服務(wù)的,得到數(shù)據(jù)模型數(shù)組后,就可以在頁(yè)面展示了。組件是在 UITableView 中展示,所以大部分情況視圖都是 UITableViewCell,如果視圖工廠得到的視圖不是 UITableViewCell,而是 UIView,則工廠會(huì)把 UIView 封裝到 UITableViewCell 中。

視圖模型注冊(cè)表

視圖模型注冊(cè)表是一個(gè)字典,前面已經(jīng)得到了數(shù)據(jù)模型數(shù)組,數(shù)組中每個(gè)元素都是一個(gè){key : model}字典,通過 key 值可以找到對(duì)應(yīng)的視圖模型的 Class Name,圖 14 是搜索模塊的視圖模型注冊(cè)表。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 14 視圖模型注冊(cè)表

視圖模型池

視圖模型池不是一個(gè)類、文件、模塊,而是多個(gè)視圖的集合。視圖模型池至少包含了視圖模型映射表中所需要的視圖模型。

視圖模型除了聲明一些業(yè)務(wù)需要的屬性之外,還需要實(shí)現(xiàn)一個(gè)方法 - (void)configSubViewsWithModel:(id)model。對(duì)外暴露的方法只有一個(gè),但實(shí)現(xiàn)上會(huì)根據(jù) model 的具體數(shù)據(jù)類型,進(jìn)行不同的處理。

組件模型轉(zhuǎn)換

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 15 組件生產(chǎn)全過程

組件模型轉(zhuǎn)換過程就是通過數(shù)據(jù) key 值從視圖模型池中查詢模型 Class Name 的過程。如圖 15 中的前 3 個(gè)步驟:

resultSearchList 是數(shù)據(jù)模型工廠的輸出結(jié)果,其中每個(gè)元素都是一個(gè){key : model}字典; 獲取到 indexPath.row 位置的元素,解析得到 key 和 model 數(shù)據(jù); 通過 key 值,通過視圖模型映射表得到視圖的 Class Name。

組件生產(chǎn)

組件的生產(chǎn)過程就是通過數(shù)據(jù)模型適配器、視圖摸適配器,得到視圖的過程。

完成數(shù)據(jù)模型適配器的工作后,在 UITableView 的協(xié)議方法 tableView:cellForRowAtIndexPath:方法中實(shí)現(xiàn)視圖的生成,如圖 18 中的后面三個(gè)步驟:

根據(jù) Class Name,生成視圖對(duì)象,并且利用視圖方法配置數(shù)據(jù); 假如視圖是 UITableViewCell 類型,則直接返回視圖對(duì)象; 否則如果視圖只是 UIView 類型,則把 view 加載到 UITableViewCell 中,然后返回 cell 實(shí)例。

如此,便生產(chǎn)出配置了數(shù)據(jù)的組件。

組件池

組件池是按照功能劃分的,在搜索框架中,組件池中的組件是所有搜索頁(yè)面可能使用的組件。其中每一個(gè)組件不但包括數(shù)據(jù)模型、視圖模型,還包括注冊(cè)表和適配器中的處理。如圖 16 所示,前面的介紹從按照模塊劃分的,而每一個(gè)紅框都是一個(gè)組件,如熱詞組件。

58 同城 iOS 客戶端搜索模塊組件化實(shí)踐

圖 16 組件池

增加組件

隨著業(yè)務(wù)發(fā)展和需求的增加,不可避免會(huì)需要增加一些新的組件。原則是能復(fù)用盡量復(fù)用,不能復(fù)用也不必要把組件做的太耦合。

首先按照接口數(shù)據(jù),創(chuàng)建一個(gè)新的數(shù)據(jù) model,包括屬性和解析方法 generateModelWithOriginalDict:; 在數(shù)據(jù)模型映射表中增加一個(gè)新的鍵值對(duì),key 值不要重復(fù),value 就是 model 的 Class Name; 根據(jù) UI 圖開發(fā)新的視圖模型,并且完成對(duì)數(shù)據(jù)的配置方法 configSubViewsWithModel: 在視圖模型映射表中增加一個(gè)新的鍵值對(duì),key 值與 2 相同,value 就是 3 中新建的視圖模型的 Class Name。

如此,便完成了組件的增加。之后可以根據(jù)數(shù)據(jù)的不同配置,讓組件可以任意的排列組合。

總結(jié)

搜索框架的優(yōu)化是一個(gè)持續(xù)的過程。當(dāng)舊的框架無法滿足業(yè)務(wù)的快速發(fā)展時(shí),需要對(duì)搜索框架進(jìn)行大規(guī)模的重構(gòu),來解決舊代碼冗余、不易復(fù)用、不易移植、擴(kuò)展難的問題;大部分情況下,是進(jìn)行小規(guī)模的優(yōu)化、擴(kuò)展以滿足需求的迭代。由此推廣到其他的模塊,其經(jīng)驗(yàn)是通的。

模塊/框架系統(tǒng)化設(shè)計(jì)。綜合考慮各種可能會(huì)接入的業(yè)務(wù)以及當(dāng)前業(yè)務(wù)的擴(kuò)展。比如搜素模塊從剛開始的一個(gè)業(yè)務(wù)線接入及定制化到多個(gè)業(yè)務(wù)線的接入及定制化,所帶來系統(tǒng)的復(fù)雜性會(huì)有根本變化。設(shè)計(jì)的時(shí)候一定不要脫離具體的業(yè)務(wù)和問題,以解決具體的問題為出發(fā)點(diǎn)。

善用組件化。頁(yè)面是一個(gè)盒子,組件是盒子里面的擺放的物品,數(shù)據(jù)指明把哪些物品如何擺放。人操作數(shù)據(jù),每個(gè)人都可以在盒子里擺出任意排列的物品。所以數(shù)據(jù)是業(yè)務(wù)有關(guān)的,組件是通用的、可復(fù)用的、低耦合的。

及時(shí)優(yōu)化和重構(gòu)。不要把問題拖到最后,否則小范圍的修改可能會(huì)產(chǎn)生大量的問題,問題會(huì)爆炸性地爆發(fā),不得不做大規(guī)模重構(gòu)。

來自:http://blog.csdn.net/csdnnews/article/details/78088447

標(biāo)簽: IOS
相關(guān)文章:
主站蜘蛛池模板: 一区二区三区毛片免费 | 99pao在线视频精品免费 | 国产在线精品成人一区二区三区 | 国产精品久久二区三区色裕 | 欧美日韩视频在线观看高清免费网站 | 亚洲精品福利视频 | 成年人小视频在线观看 | 国产大片91精品免费观看男同 | 偷拍第一页| 久久七| 国产情趣酒店鸳鸯浴在线观看 | 在线午夜 | 欧美一区二区三区免费不卡 | 国产国产人免费人成免费视频 | 亚洲欧美日韩中文综合v日本 | 黄网视频在线观看 | 日韩欧美在线综合 | 成人资源在线 | 天天干影院 | 久草热视频 | 国产三区视频在线观看 | 亚洲a免费 | 成人看片黄a免费看视频 | 国产精品亚洲va在线观看 | 亚洲一区二区三区久久 | aaaaaa国产毛片孕妇版 | 在线亚洲播放 | 日本黄色影片在线观看 | 国内视频在线 | 在线观看国产一区 | 亚洲国产日韩欧美在线a乱码 | 亚洲 欧美 日韩在线 | 综合在线视频 | 丁香婷婷亚洲六月综合色 | 国产精品乱码免费一区二区 | 亚洲精品一二三区 | 精品国产成人在线 | 91福利专区 | 91制服丝袜在线 | 国产日韩欧美亚洲青青草原 | 免费成人在线网站 |