1. 程式人生 > IOS開發 >iOS開發小記-設計模式(持續更新)

iOS開發小記-設計模式(持續更新)

MVC、MVP、MVVM


關於三者的比較說明,可以看Casa醬的這篇iOS應用架構談 view層的組織和呼叫方案,巧神的被誤解的 MVC 和被神化的 MVVM篇幅有限,也可以瞅一眼~

原型模式


原型模式:使用原型例項指定建立物件的種類,並通過複製這個原型建立新的物件。

它是一個非常簡單的設計模式,基於“複製”操作。複製指用同一模具生產一系列的產品。模具所基於的物品稱為原型。 此模式的最低限度是生成物件的真實副本,以用作同一環境下其他相關事物的基礎。

  • 何時使用

通俗來說,如果要建立的新物件和自身差異並不大,其行為略有不同,且複製自身比手工例項化要好,就可以使用原型模式。

例如我有一個Person

例項A,我需要一個跟A僅Name不同,但是其餘屬性均相同的新物件B,如果使用者通過新建例項並將A的所有屬性賦值給B,這無疑是相當麻煩的,這時我們就可以使用B = [A clone]來複制A的屬性,從而節省大量精力,提高了複用性也便於維護。

  • Cocoa中的應用

最常見的應用就是深複製,如NSMutableArray = [NSArray mutableCopy]

注:淺複製不屬於原型模式,它不滿足上面說到的原型模式的最低限度,僅僅是指標拷貝。
複製程式碼
  • 實際開發中的應用

比較常見的情況就是對自定義物件進行拷貝的時候,我們需要通過對自定義物件實現NSCopying協議及其方法- (id)copyWithZone:(NSZone *)zone

單例模式


單例模式:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。它幾乎是所有設計模式中最簡單的一個了。

  • 何時使用
  1. 類只能有一個例項,而且必須從一個為人熟知的訪問點對其進行訪問。
  2. 這個唯一的例項只能通過子類化進行擴充套件,而且擴充套件的程式碼不會破壞客戶端的程式碼。 一般來講,工程中如果需要頻繁使用某一個類,而且可以只需要一個該類的例項,並且通過它共享資源,那麼我們就可以考慮使用單例模式。
  • Cocoa中的應用

最常見的就是[UIApplication sharedApplication]

  • 實際開發中的應用

例如一些管理類xxManager,設定類xxConfiger

等等,都是會頻繁使用並且需要共享資源儲存臨時狀態,就可以使用單例。 關於多執行緒下加鎖保證只建立一個例項,我們可以使用@synchronized、NSLock等,更推薦使用dispatch_once,效能更優。 甚至我們還可以使用巨集來實現單例。

  • Objective-C下的問題

由於Objective-C並不能像C++,Java中一樣能夠隱藏建構函式,小夥伴們還是可以alloc/init來建立物件,一般有兩種解決方案:

  1. 從OC的物件建立角度出發,就是把建立物件的各種入口給封死了。alloc,copy等等,無論是採用哪種方式建立,我都保證給出的物件是同一個。
  2. 溫柔派就直接告訴外面,alloc,new,copy,mutableCopy方法不可以直接呼叫。否則編譯不過。

工廠方法模式


工廠方法模式:也叫做虛構造器。定義建立物件的介面,讓子類決定例項化哪一個類。工廠方法使得一個類的例項化延遲到其子類。

通俗來講,對於有多種類似的產品,我們可以將產品的公有屬性和方法抽象為一個產品基類,將生產產品的工廠抽象為一個工廠基類,並暴露返回產品物件的工廠方法,各自產品的工廠繼承自工廠基類,並重載該工廠方法以返回對應的產品物件。

  • 何時使用
  1. 編譯時無法準確預期要建立的物件的類。
  2. 類想讓子類決定執行時建立什麼。
  3. 類有若干個輔助類為其子類,而你想將返回哪個子類這一資訊區域性化。
  • 實際開發中的應用

例如一開始的股票詳情介面,有指數、滬深、港股、美股等等,分別有一個基於TKHqBaseStockDetailViewController的詳情子類,以及基於TKHqBaseStockDetailFactory的工廠子類,工廠提供了一個建立詳情的方法,使用者可以使用對應的工廠子類來獲取想要的詳情子類。

注:我們也可以通過傳遞股票資訊在工廠內部通過列舉來直接建立對應的詳情子類,而不通過工廠子類來建立,這種就是簡單工廠。
複製程式碼

抽象工廠模式


抽象工廠模式:提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類。

通俗來講,我們可以提供一個工廠基類,其中包含了建立一系列產品的工廠方法,並且通過使用預處理或者列舉來生成不同的工廠子類,這些工廠子類中都實現了基類的工廠方法,並根據自己的需求在創造產品的工廠方法中獲取想要的具體的產品子類。

  • 與工廠方法的區別
  1. 抽象工廠使用物件組合來建立抽象產品,工廠方法是通過繼承來建立抽象產品。
  2. 抽象工廠用於建立多系列產品,工廠方法用於建立一個產品。
  3. 如果要支援新的產品,抽象工廠需要修改基類,工廠方法則是增加新的工廠子類。
  • 何時使用

當我們需要的不僅僅是一個產品,而是一個包含了多個相關聯或相依存的不同產品結構的產品族,且我們不想指定具體的類或者建立的細節。

  • Cocoa中的應用

最常見的就是NSNumberNSStringNSArrayNSDictionary。其中NSNumber中的numberWithBool:生成的實際型別是NSCFBoolean型別,而其他的大多數生成的是NSCFNumber型別,並且都實現了抽象超類NSNumber的所有定義。這裡的NSCFBooleanNSCFNumber就相當於兩個工廠子類,超類的所有定義就相當於一系列產品,Fundation框架中抽象工廠的這種特點,被稱為類簇

類簇是Fundation框架中的一種非常常見的設計模式,基於抽象工廠的思想。它將若干個私有具體工廠子類集合到一個公有的抽象超類之下。例如NSNumber有一系列公有API,定義了各種型別資料的共有行為,我們在使用時不用關心NSNumber例項的具體型別。

注:類簇中往往提供了許多生成工廠子類的方法,這些方法不應該在具體的工廠子類中過載,工廠子類只應該關注產品的建立。
複製程式碼
  • 實際開發中的應用

例如前面說到的股票詳情,一旦詳情頁面的變得複雜,往往需要由多種介面元素來組成,並且隨著股票種類的增加,我們可能並不想要在關注我們用什麼具體的工廠子類去例項化詳情,這裡我們就可以使用抽象工廠。

可以將詳情大致劃分為四層:Nav資訊層、基礎盤口層、圖表層和資訊層。定義一個抽象超類詳情TKHqStockDetailViewController,定義一系列基於這四層的公有API,並且宣告+ (TKHqStockDetailViewController *)detailWithStock:(TKHqStockModel *)stockModel的類工廠方法,該方法中通過股票型別建立了不同的“詳情工廠”,最終在各自的工廠子類中實現一系列的API。

介面卡模式


介面卡模式:將一個類的介面轉換成客戶希望的另外一個介面。介面卡模式使得原來由於介面不相容而不能一起工作的那些類可以一起工作。

基本上有兩種實現介面卡的方式:類介面卡和物件介面卡。

類介面卡:通過多繼承來實現,在OC中可以通過協議和單繼承模擬多繼承。協議用於規範統一的介面,父類用於實現具體的新介面,子類通過過載協議的目標介面,在介面內部呼叫父類的新介面來響應處理。

物件介面卡:與類介面卡不同,它不需要繼承被適配者,僅僅需要實現統一的協議,並持有一個被適配者的引用,在過載的協議介面中,呼叫該引用的新介面來響應處理。

兩種方式的區別如下:

  1. 類介面卡只針對單一具體的被適配者,不適用於被適配者的子類,否則需要新建介面卡;物件介面卡可以適配多個被適配者,如果要適配它們的子類,宣告一個子類引用即可。
  2. 類介面卡易於過載被適配者的行為,而物件介面卡不能過載,否則需要新建被適配者的一個子類,再持有這個子類例項的引用。
  3. 類介面卡無需使用額外的指標間接訪問被適配者,而物件介面卡需要。
注:一般來說,物件介面卡適用範圍更廣泛,對於未來可能新增的需要適配的新介面,也更容易擴充套件。
複製程式碼
  • 何時使用物件介面卡
  1. 已有類的介面與需求不匹配。
  2. 想要一個可複用的類,該類能夠同可能帶有不相容介面的其他類協作。
  3. 需要適配一個類的幾個不同子類,可是讓每一個子類去子類化一個類介面卡又不現實。
  • 實際開發中的使用

例如詳情的設定頁面,我們發現Cell的結構基本差不多,可以由Title、SubTitle、Detail、AccessoryView四部分組成,其中某些Cell可能顯示SubTitle有的不顯示;有的顯示Detail有的不顯示;AccessoryView內容可能是個箭頭,有的可能是個SwitchBar。如果我們對每種樣式去定製化一個Cell,無疑是難以維護和擴充套件的,這裡我們就可以通過介面卡模式,將每種設定的資料適配成規範資料來統一處理。

我們可以建立一個Protocol來宣告目標介面,包括了Title、SubTitle、Detail、AccessoryView四部分內容,然後定義一個Adapter實現該協議,將使用到各種被適配者(在這裡也就是不同設定型別原來使用的Model)持有引用,然後目標介面中判斷哪種引用不為nil,然後根據當前被適配者的介面來獲取相應的資料,這樣外部在使用不同設定型別時,只需要將原來的Model傳給Adapter,Cell便能通過Adapter來獲取統一規範的資料。

注:也可以通過實現Protocol 、整合各自Model的具體子類的類介面卡方式來實現上述功能,這樣子Adapter中就只處理父類Model的資料,在使用時不同設定給Cell設定不同的子Adapter即可。
複製程式碼

委託模式


委託模式:主要是介面卡模式,它實際上同樣是把類的介面變換為客戶端要求的另一種介面,通過Protocol來要求其他類適配它需要的介面,而它本身就是個介面卡。

  • 何時使用

當我們需要其他類來做一些當前類無法完成的事情,或者需要使用者來處理某些個性化操作。 一般可以通過delegate或者block來實現。

  • Cocoa中的使用

最常見的就是UITableView中delegate和datasource的使用,用來使用者自定義某些設定和響應事件。

  • 實際開發中的應用

例如需要傳遞上一層資料、需要使用者來定義某些設定、View需要VC來響應push/pop等等。

代理模式


代理模式:為其他物件提供一種代理以控制對這個物件的訪問。其思想是使用一個基本上跟實體物件行為相同的代理。

  • 何時使用
  1. 需要一個遠端代理,為位於不同地址空間或網路中的物件提供本地代表。
  2. 需要一個虛擬代理,來根據要求建立重型的物件。
  3. 需要一個保護代理,來根據不同訪問許可權控制對原物件的訪問。
  4. 需要一個智慧引用代理,通過對實體物件的引用進行計數來管理記憶體。也能用於鎖定實體物件,讓其他物件不能修改它。
  • Cocoa中的應用

Objective-C中的delegate屬於委託模式,但也基於了代理模式的思想,如第四點所說,也相當於一個智慧引用代理,同時通過協議介面的控制,也一定程度上相當於控制了對原物件的訪問許可權。

  • 實際開發中的應用

NSProxy

橋接模式


橋接模式:將抽象部分與它的實現部分分離,使它們都可以獨立地變化。 通俗來講,如果實現系統可能有多角度分類,每一種分類都有可能變化,那麼就把這種多角度分離出來讓它們獨立變化,較少耦合。

  • 何時使用
  1. 不想在抽象與其實現之間形成固定的繫結關係(這樣就能在執行時切換實現)。
  2. 抽象及其實現都應可以通過自子類化獨立進行擴充套件。
  3. 對抽象的實現進行修改不應該影響客戶端程式碼。
  4. 如果每個實現需要額外的子類細化抽象,則說明有必要把它們分成兩部分。
  5. 想在帶有不同抽象介面的多個物件之間共享一個實現。

觀察者模式


觀察者模式:定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新。也叫做釋出-訂閱模式。

  • 何時使用
  1. 有兩種抽象型別相互依賴。將它們封裝在各自的物件中,就可以對他們單獨進行改變和複用。
  2. 對一個物件的改變需要同時改變其他物件,而不知道具體有多少物件有待改變。
  3. 一個物件必須通知其他物件,而它又不知道需知道其他物件是什麼。
  • Cocoa中的應用

有兩種技術都使用了觀察者模式:通知和KVO。

中介者模式


中介者模式:用一個物件來封裝一系列物件的互動方式。中介者使各物件不需要顯示地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。

  • 何時使用
  1. 物件間的互動雖定義明確然而非常複雜,導致一組物件彼此相互依賴而且難以理解。
  2. 因為物件應用了許多其他物件並與其通訊,導致物件難以複用。
  3. 想要定製一個分部在多個類中的邏輯或者行為,又不想生成太多子類。
  • 實際開發中的使用

在多模組的開發中,由於不同產品模組由不同小組開發,相互之間的程式碼相對封閉,但是由於業務關聯,往往又需要跟其他模組互動,比如A模組開啟B模組頁面,B模組又需要查詢C模組的資料,一般實現可能直接A呼叫B模組相關程式碼,B模組呼叫C模組相關程式碼,這樣造成的問題一是模組之間相互耦合,獨立開發十分困難,需要遮蔽相關程式碼;二是一旦其他模組的API修改,相關模組全都得改動。

針對這個問題,可以使用中介者模式,框架定義了一箇中介者基類baseMediator,並實現了一套協議用於接收外部事件,A模組如果想要與B模組互動,可以通過一個通知方法告訴BMediator,然後BMediator根據不同引數來處理相應事件,這樣避免了模組之間的強耦合,同時外部模組也不在關心它的內部實現,只需要關心其暴露出來的事件定義。