文章目錄
- 設計模式:解決編程問題的設計範本
- 最重要的設計模式:模型-視圖-控制器
- 使用設計模式解決問題
Title:採用設計模式使您的應用程式合理化
採用設計模式使您的應用程式合理化
在 Objective-C 編程中,繼承是添加應用程式特定行為的一種方式。建立的現有類的子類,要麼增加超類的屬性和行為,要麼在某種程度上修改它們。但是,它也有其他更加動態方式,可添加應用程式特定行為,而不涉及到子類化。這些動態技巧和方式,是基於設計模式的。正如本文章所解釋的,在代碼中採用設計模式,有助於增加類和架構類的可再用性和擴充性。
設計模式:解決編程問題的設計範本
設計模式是一個抽象工具,用於物件導向的軟體開發,以及其他領域。它是一個設計範本,在特定背景中,解決一般性的、重複出現的問題。因此,設計模式是一種針對特定的、具體的設計的準則:在某種意義上,它是模式的“執行個體化”。在如何應用設計模式上,有一定的靈活性,通常例如程式設計語言和現有架構等事物,會影響如何應用模式。
有幾個設計主題或原則對設計模式產生影響。這些設計原則是構建物件導向系統的經驗法則,例如“封裝發生變化的系統結構方面”(encapsulate the aspects of system structure that vary) 和“面向介面編程,而非面向實現編程”(program to an interface, not an implementation)。它們表達了重要的見解。例如,封裝原則告訴我們,如果隔離並封裝系統中發生變化的部分,它們可以獨立於系統其他部分進行變化,特別是如果您為它們定義了不依賴實現特性的介面。您稍後可以修改或擴充這些可變部分,而不影響系統的其他部分。這樣一來,您清除了各部分之間的互相依賴,減少了各部分的耦合性,系統就會變得更加靈活、更容易修改。
這樣的優點,讓設計模式成了編寫軟體的重點考慮因素。如果您在應用程式的設計中找到了模式,加以調整和使用,該程式(以及其所包含的對象和類)在將來需要時複用程度更高、擴充能力更強和更容易修改。此外,基於設計模式的應用程式,與沒有基於設計模式的應用程式比較,會更見優雅和更具效率,因為它們只需較少的代碼就能達到同樣目的。
您會發現,設計模式的應用貫穿於整個 Cocoa Touch 和 Cocoa 架構、Objective-C 的運行時及程式設計語言自身。您可以幾乎“免費”獲得部分基於模式的機制,而其他部分則需要您做一些工作。情況合適時,可以將設計模式應用到您自己的應用程式代碼中。如果使用與 Cocoa Touch 和 Cocoa 架構相同的模式,您的代碼往往會更好地與架構的代碼匹配,運行也更為優雅。
最重要的設計模式:模型-視圖-控制器
“模型-視圖-控制器”(Model-View-Controller) 設計模式,通常被稱為“MVC”,將以下一種角色指派給應用程式中的對象:“模型”、“視圖”或“控制器”。模式不僅定義了對象在應用程式中扮演的角色,還定義了對象之間通訊的方式。這三類對象的每一個,都由抽象邊界與其他對象分隔,穿過這些邊界與其他類型的對象進行通訊。應用程式中某一 MVC 類型的對象的集合,有時統稱為層,例如模型層。
對於任何 iOS 應用程式或 Mac 應用程式而言,MVC 對一個好的設計至關重要。採用此設計的好處多不勝數。這些應用程式中的很多個物件,傾向於更可再用,它們的介面傾向於定義得更好。採用 MVC 設計的應用程式,也比其他應用程式更容易擴充。此外,您的應用程式可以用到的很多技術和架構,都是基於 MVC 的,也要求您的自定對象扮演其中一個 MVC 角色。
您可能還沒有意識到,就已經建立了一個基於 MVC 的應用程式:您的首個 iOS 應用程式中的 HelloWorld。模型對象是 userName 屬性(NSString 對象),由 HelloWorldViewController 類聲明和管理。HelloWorldViewController 類和 HelloWorldAppDelegate 類的執行個體,是應用程式的控制器對象;而應用程式的視圖對象,是文本欄、標籤、按鈕和背景視圖。
有關“模型-視圖-控制器”的完整資訊,請參閱 Concepts in Objective-C Programming(Objective-C 編程中的概念)中的“Model-View-Controller”。
模型對象
模型對象封裝了應用程式的資料,並定義操控和處理該資料的邏輯和運算。例如,模型對象可能是表示遊戲中的角色或地址簿中的連絡人。有時應用程式的模型層,實際是相關對象的一個或多個圖形。資料載入應用程式後,作為應用程式的持續狀態(不論該持續狀態是儲存在檔案中,還是在資料庫中)一部分的大部分資料,應該駐留在模型對象中。因為模型對象代表了與特定問題領域相關的知識和專長,在相似問題領域,就可以重複使用它們。“純”模型對象應該和視圖對象不發生明確串連(視圖對象顯示其資料並允許使用者編輯資料),它不該管到使用者介面和顯示的問題。
使用者在視圖層中所進行的建立或修改資料的操作,通過控制器對象傳達出去,最終會建立或更新模型對象。模型對象更改時(例如通過網路連接接收到新資料),它通知控制器對象,控制器對象更新相應的視圖對象。
視圖對象
視圖對象是應用程式中使用者可以看見的對象。視圖對象知道如何將自己繪製出來,並可能對使用者的操作作出響應。視圖對象的主要目的,就是顯示來自應用程式模型對象的資料,並使該資料可被編輯。儘管如此,在 MVC 應用程式中,視圖對象通常與模型對象分離。
因為您通常重新使用並重新設定對象,視圖對象保證了應用程式之間的一致性。對於 iOS,UIKit 架構提供了視圖類的集合;對於 OS X,AppKit 架構提供了類似的集合。在 UIKit 中,視圖對象最終繼承自 UIView 類;在 AppKit 中,視圖對象最終繼承自 NSView 類。
視圖對象通過應用程式的控制器對象,瞭解模型資料的更改,並通過控制器對象,將使用者發動的修改(例如,在文本欄輸入的文本),傳達到應用程式的模型對象。
控制器對象
在應用程式的一個或多個視圖對象和一個或多個模型對象之間,控制器對象充當媒介。控制器對象因此是同步管道程式,通過它,視圖對象瞭解模型對象的更改,反之亦然。控制器對象還可以為應用程式執行設定和協調任務,並管理其他對象的生命週期。
控制器對象解釋在視圖對象中進行的使用者操作,並將新的或更改過的資料傳達給模型對象。模型對象更改時,一個控制器對象會將新的模型資料傳達給視圖對象,以便視圖對象可以顯示它。
使用設計模式解決問題
物件導向的系統(例如應用程式)是動態。對象在運行時所能做的,並不局限於編寫時所設定的行為。一個對象可以向另一個對象發送訊息,而同一訊息的目標,會根據運行時的情況而變化。一個對象也可以在運行時與可變的一組其他對象合作,並使用多種技巧,有效地完成應用程式的工作。一個對象或一群對象要這樣做,必須利用許多技巧和架構架構,它們都是設計模式的派生。
下面部分說明許多這樣的技巧和架構。請將它們作為您 Objective-C 編程工具箱的一部分。
委託:代表另一個對象
在委託中,一個稱為委託的對象應另一個對象的請求,作為該對象的代表。作出委託的對象,通常是架構模型。在執行的某些時候,它會向其委託發送訊息,告訴委託即將發生某些事件,並要求給它回應。委託(通常是自定類的執行個體)實施供該訊息調用的方法,並返回相應的值。通常該值是一個 Boolean 值,告訴作出委託的對象是否繼續操作。
委託因此是一種將應用程式特定行為加入架構類工作的手段,而無需給該類建立子類。它是一種常見的、強大的設計,來擴充和影響架構的行為。
您應該記得,在編寫您的首個 iOS 應用程式 HelloWorld 時,建立了 HelloWorldAppDelegate 對象。Xcode 自動將其分配為應用程式物件(為架構對象)的委託。應用程式委託可以處理 application:didFinishLaunchingWithOptions:,以及應用程式物件發送給它的其他委託訊息。
有兩個可程式化的組件用於委託。委託類必須定義屬性(通過名稱為 delegate 的約定),以儲存一個指向委託的參考。它還必須聲明委託類必須採用的協議(請參閱以下部分以獲得有關協議的更多資訊)。Cocoa Touch 和 Cocoa 架構的許多類,都提供委託作為一種方式,給應用程式用來增加其特定的架構行為。
但是委託並不局限於架構類。您可以在應用程式的兩個自定對象之間實施委託。Cocoa Touch 應用程式常見的設計,是將委託作為一種手段,允許子視圖控制器將某些值(通常為使用者輸入的值)傳達到父視圖控制器。
協議:使不相關的對象之間能通過繼承進行通訊
協議是可程式化介面的聲明,任何類都可以實施它的方法。與協議相關聯的類執行個體,調用協議的方法,並擷取由該類正式採用和實現該協議所返回的值。對象之間的此類通訊,產生了一個特定目標,例如解析 XML 代碼或拷貝對象。協議介面兩邊的對象可以通過繼承,實施遠距離彼此相關。協議因此和委託一樣,可作為子類化的替換手段,通常是架構實施委託的一部分。
Apple 提供的架構,聲明了數十個協議。此外,您的應用程式可以聲明自定協議,讓類可以採用。協議是您編程工具箱的一部分。Programming with Objective-C(使用 Objective-C 編程)對協議進行了綜合描述。
通知中樞:通知對事件感興趣的觀察者
通知中樞是 Foundation 架構的一個子系統,它嚮應用程式中註冊為某個事件觀察者的所有對象廣播訊息(即通知)。(從編程角度而言,它是 NSNotificationCenter 類的執行個體)。該事件可以是發生在應用程式中的任何事情,例如進入後台狀態,或者使用者開始在文本欄中鍵入。通知是告訴觀察者,事件已經發生或即將發生,因此讓觀察者有機會以合適的方式響應。通過通知中樞來傳播通知,是增加應用程式物件間合作和內聚力的一種途徑。
例如,iOS 應用程式中的視圖控制器,可以觀察 UIKeyboardWillShowNotification 通知,以調整其視圖的幾何圖形,來容納虛擬鍵盤。正如此例所示,通知是一個對象,該對象的名稱指明了一個特定事件,以及該事件是已經發生或將要發生。它還將一個引用(指向發布或發送通知的對象)送到通知中樞,而它可以包含補充資訊字典。
任何對象都可以觀察通知,但要做到這一點,該對象必須註冊,以接收通知。在註冊時,它必須指定選取器,以確定由通知傳送所調用的方法;方法簽名必須只有一個參數:通知對象。註冊後,觀察者也可以指定發布對象。
通知中樞的通知跟委託訊息相似;當某些事件發生時,兩者都發送給任意對象。但是,處理通知的方法與委託方法不同,它不能傳回值。通過通知中樞的通知是同步的,與委託一樣。
應用程式的自定對象可定義和發布自己的通知,其他自定對象則可以觀察該通知。
目標-操作:事件發生時封裝待發送的訊息
目標-操作設計在概念上很簡單。一個對象儲存著組成訊息運算式的元素,某些事件發生時,將這些元素放在一起,並發送一則訊息。這些元素為一個選取器,用來確定訊息(即操作)和接收訊息的對象(即目標)。目標的類會實現與操作和目標相對應的方法,當它在運行中接收到訊息時,會通過執行方法來響應事件。
目標-操作主要是 Cocoa Touch 和 Cocoa 架構中的一種控制功能。控制是使用介面物件,例如使用者通過輕按、拖移等進行操控的按鈕、滑塊或開關,將使用者的意圖通過訊號發送給應用程式。Cocoa Touch 的控制儲存了操作和目標;大多數 Cocoa 控制與一個或多個單元對象進行了配對,這些單元對象儲存了目標和操作。
一些架構在對象中使用目標-操作而不是控制。例如,在設計手勢辨識器時,UIKit 架構使用了目標-操作。手勢識別對象識別手勢後,它將操作訊息發送給目標對象。
索引值觀察:值更改時通知觀察者
索引值觀察(Key-value observing,或簡稱 KVO)允許對象觀察另一個對象的屬性。該屬性值改變時,會通知觀察對象。它瞭解新值以及舊值;如果觀察的屬性為對多的關係(例如數組),它也要瞭解哪個包含的對象發生了改變。KVO 有助於使應用程式變得更內聚,保持模型、控制器和視圖層中的對象與改變同步。
與 NSNotificationCenter 通知相似,多個 KVO 觀察者可以觀察單一屬性。此外,KVO 更動態,因為它允許對象觀察任意屬性,而不需任何新的 API,例如通知名稱。KVO 是一個輕量級點對點通訊機制,不允許觀察所有執行個體的特定屬性。
基於設計模式的其他架構設計
Cocoa Touch 和 Cocoa 架構也包含基於設計模式的其他設計,有以下模式:
視圖層次。應用程式所顯示的視圖,會排列成階層(直觀上基於包含)。此模式允許應用程式將單個視圖和合成視圖同等對待。層次的根部為一個視窗對象;根部以下的每個視圖,都有一個父視圖,以及零個或多個子視圖。父視圖包含子視圖。視圖層次是繪圖和事件處理的結構性組件。響應器鏈。響應器鏈是一系列的對象(主要是視圖,但也有視窗、視圖控制器和應用程式物件本身),事件或操作訊息可以沿著響應器鏈傳遞,直到鏈中的一個對象處理該事件。因此,它是一個合作性事件處理機制。響應器鏈與視圖層次密切相關。視圖控制器。雖然 UIKit 和 AppKit 架構都有視圖控制器類,它們在 iOS 中尤其重要。視圖控制器是一種特殊的控制器對象,用於顯示和管理一組視圖。視圖控制器對象提供基礎結構,來管理內容相關的視圖並協調視圖的顯示與隱藏。視圖控制器管理應用程式視圖的子階層。前台。在前台模式中,應用程式所執行的工作,從一個執行環境重新導向(或彈回)到另一個環境。(執行環境是一個與主線程或輔助線程相關聯的調度隊列或操作隊列。)您將前台模式主要應用於這樣的情形:在次隊列執行的工作,產生了必須在主隊列執行的任務,例如更新使用者介面的操作。類別。類別提供了一種方式,通過將方法添加到一個類,以使該類得到擴充。與委託一樣,它可以讓您自定行為,而不子類化。類別是 Objective-C 的一個功能,在編寫 Objective-C 代碼中有說明。