在Cocoa中到處都可以找到設計模式的應用,基於模式的機制或架構在Cocoa框架和Objective-C運行環境及語言中是很常見的。Cocoa經常把自己與眾不同的工作機制建立在模式上,它的設計受到諸如語言能力或現有架構這樣因素的影響。
本部分包含設計模式:可重用的面向對象軟件的元素一書中編目的大多數設計模式的介紹。每個設計模式都有一個總結性的描述,以及該模式的Cocoa實現的討論。文中列出的都是Cocoa實現的模式,每個模式的討論都發生在特定的Cocoa環境中。我們推薦您熟悉這些模式,您會發現這些模式在Cocoa軟件開發中非常有用。
Cocoa中設計模式的實現有不同的形式。下面部分中描述的一些設計—比如協議和范疇—是Objective-C語言的特性;在另外一些場合中,“模式的實例”被實現為一個類或一組相關的類(比如類簇和單件類);還有一些場合下,模式表現為一個大的框架結構,比如響應者鏈模式。對於某些基於模式的機制,您幾乎可以“免費”使用;而另外一些機制則要求您做一些工作。即使對於Cocoa沒有實現的模式,我們也鼓勵您在條件許可的情況下自行實現,比如在擴展類的行為時,對象合成(裝飾模式)技術通常就比生成子類更好。
有兩個設計模式沒有在下面的內容中進行討論,即模型-視圖-控制器(MVC)模式和對象建模。MVC是一種復合或聚合模式,就是說它基於幾種不同類型的模式。對象建模在四人組的分類目錄中沒有對應類別,它源自關系數據庫領域。然而,MVC和對象建模在Cocoa中可能是最重要和最普遍的設計模式或用語,而且它們在很大程度上是相關的。它們在幾個技術的設計中發揮關鍵的作用,包括綁定、撤消管理、腳本控制、和文檔架構。要了解更多有關這些模式的信息,請參見"模型-視圖-控制器設計模式"和"對象建模"部分。
本部分包含如下主要內容:
抽象工廠模式
適配器模式
責任鏈模式
命令模式
合成模式
裝飾模式
表觀模式
跌代器模式
仲裁者模式
備忘錄模式
觀察者模式
代理模式
單件模式
模板方法模式
抽象工廠模式
提供一個接口,用於創建與某些對象相關或依賴於某些對象的類家族,而又不需要指定它們的具體類。通過這種模式可以去除客戶代碼和來自工廠的具體對象細節之間的耦合關系。
類簇
類簇是一種把一個公共的抽象超類下的一些私有的具體子類組合在一起的架構。抽象超類負責聲明創建私有子類實例的方法,會根據被調用方法的不同分配恰當的具體子類,每個返回的對象都可能屬於不同的私有子類。
Cocoa將類簇限制在數據存儲可能因環境而變的對象生成上。Foundation框架為NSString
、NSData
、NSDictionary
、NSSet
、和NSArray
對象定義了類簇。公共超類包括上述的不可變類和與其相互補充的可變類NSMutableString
、NSMutableData
、NSMutableDictionary
、NSMutableSet
、和NSMutableArray
。
使用和限制
當您希望創建類簇代表的類型的可變或不可變對象時,可以使用類簇中的某個公共類來實現。使用類簇是在簡潔性和擴展性之間進行折衷。類簇可以簡化類接口,因此使其更易於學習和使用,但是創建類簇抽象類的定制子類則會變得更加困難。
適配器模式
將一個類接口轉化為客戶代碼需要的另一個接口。適配器使原本由於兼容性而不能協同工作的類可以工作在一起,消除了客戶代碼和目標對象的類之間的耦合性。
協議
協議是一個編程語言級別(Objective-C)的特性,它使定義適配器模式的實例成為可能(在 Java中的“接口”和“協議”是同義的)。如果您希望一個客戶對象和另一個對象進行交流,但由於它們的接口不兼容而導致困難,您就可以定義一個協議,它本質上是一系列和類不相關聯的方法聲明。這樣,其它對象的類就可以正式采納該協議,並通過實現協議中的全部方法來“遵循”該協議。結果,客戶對象就可以通過協議接口向其它對象發送消息。
協議是一組獨立於類層次的方法聲明,這樣就有可能象類的繼承那樣,根據對象遵循的協議對其進行分組。您可以通過NSObject
的conformsToProtocol:
方法來確認一個對象的協議關系。
除了正式協議之外,Cocoa還有一個非正式協議的概念。這種類型的協議是NSObject
類中的一個范疇(category),這樣就使所有的對象都成為范疇方法的潛在實現者(參見"范疇"部分)。非正式協議的方法可以選擇性地實現。非正式協議是委托機制實現的一部分(參見"委托"部分。
請注意,協議的設計和適配器模式並不完全匹配。但它是使接口不兼容的類在得以協同工作的手段。
使用和限制
協議主要用於聲明層次結構上不相關的類為了互相通訊而需要遵循的接口。但是,您也可以將協議用於聲明對象的接口,而隱藏相應的類。Cocoa框架包括很多正式協議,這些協議使定制子類可以和框架進行特定目的的通訊。舉例來說,Foundation框架中包含NSObject
、NSCopying
、和NSCoding
協議,都非常重要。Application Kit中的協議包括NSDraggingInfo
、NSTextInput
、和NSChangeSpelling
。
正式協議要求遵循者實現協議聲明的所有方法。它們也是零碎的,一旦您定義一個協議並將它提供給其它類,將來對協議的修改會使那些類不能工作。
責任鏈模式
通過為多個對象提供處理請求的機會,避免請求的發送者和接收者產生耦合。將接收對象串成鏈,並將請求沿着接收者鏈進行傳遞,直到某個對象對其進行處理。對象或者處理該請求,或者將它傳遞給鏈中的下一個對象。
響應者鏈
Application Kit框架中包含一個稱為響應者鏈的架構。該鏈由一系列響應者對象(就是從NSResponder
繼承下來的對象)組成,事件(比如鼠標點擊)或者動作消息沿着鏈進行傳遞並(通常情況下)最終被處理。如果給定的響應者對象不處理特定的消息,就將消息傳遞給鏈中的下一個響應者。響應者在鏈中的順序通常由視圖的層次結構來決定,從層次較低的響應者對象向層次較高的對象傳遞,頂點是管理視圖層次結構的窗口對象,窗口對象的委托對象,或者全局的應用程序對象。事件和動作消息在響應者鏈中的確切傳遞路徑是不盡相同的。一個應用程序擁有的響應者鏈可能和它擁有的窗口(甚至是局部層次結構中的視圖對象)一樣多,但每次只能有一個響應者鏈是活動的—也就是與當前活動窗口相關聯的那個響應鏈。
還有一個與響應者鏈相類似的鏈,用於應用程序的錯誤處理。
視圖層次的設計應用的是合成模式(參見"合成模式"部分),它和響應者鏈密切相關。動作消息—源自控件對象的消息—基於目標-動作機制,是命令模式(參見"命令模式"部分)的一個實例。
使用和限制
當您通過Interface Builder或以編程的方式為程序構造用戶界面時,可以“免費”得到一個或多個響應者鏈。響應者鏈和視圖層次結構一起出現,當您使一個視圖對象成為窗口內容視圖的子視圖時,視圖層次結構就自動生成了。如果您將一個定制視圖加入到一個視圖層次結構中,它就變成響應者鏈的一部分;如果您實現了恰當的NSResponder
方法,就可以接收和處理事件及動作消息。定制對象是窗口對象或全局應用程序對象NSApp的委托對象,也可以接收和處理那些消息。
您也可以用編程的方式將定制的響應者對象注入到響應者鏈中,以及通過編程操作響應者在鏈中的順序。
進一步閱讀: 處理事件和動作消息及程序錯誤的的響應者鏈在Cocoa事件處理指南和Cocoa的錯誤處理編程指南文檔中進行描述。本文檔在"合成模式"部分對視圖層次結構有總結性的介紹,在"核心應用程序架構"部分有更全面的描述。
命令模式
這種模式將請求封裝為對象,使您可以用不同的請求來對客戶代碼進行參數化;對請求進行排隊和記錄,並支持可撤消(undoable)的操作。請求對象將一或多個動作綁定在特定的接收者上。命令模式將發出請求的對象和接收及執行請求的對象區分開來。
調用對象
NSInvocation
類的實例用於封裝Objective-C消息。一個調用對象中含有一個目標對象、一個方法選擇器、以及方法參數。您可以動態地改變調用對象中消息的目標及其參數,一旦消息被執行,您就可以從該對象得到返回值。通過一個調用對象可以多次調用目標或參數不同的消息。
創建NSInvocation
對象需要使用NSMethodSignature
對象,該對象負責封裝與方法參數和返回值有關系的信息。NSMethodSignature
對象的創建又需要用到一個方法選擇器。NSInvocation
的實現還用到Objective-C運行環境的一些函數。
使用和限制
NSInvocation
對象是分布式、撤消管理、消息傳遞、和定時器對象編程接口的一部分。在需要去除消息發送對象和接收對象之間的耦合關系的類似場合下,您也可以使用。
分布式對象是一種進程間通訊技術,關於這個主題的更多信息請參見"代理模式"部分。
目標-動作
目標-動作機制使控件對象—也就是象按鍵或文本輸入框這樣的對象—可以將消息發送給另一個可以對消息進行解釋並將它處理為具體應用程序指令的對象。接收對象,或者說是目標,通常是一個定制的控制器對象。消息—也被稱為動作消息—由一個選擇器來確定,選擇器是一個方法的唯一運行時標識。典型情況下,控件擁有的單元對象會對目標和動作進行封裝,以便在用戶點擊或激活控件時發送消息(菜單項也封裝了目標和動作,以便在用戶選擇時發送動作消息)。目標-動作機制之所以能夠基於選擇器(而不是方法簽名),是因為Cocoa規定動作方法的簽名和選擇器名稱總是一樣的。
使用和限制
當您用Interface Builder構建程序的用戶界面時,可以對控件的動作和目標進行設置。您因此可以讓控件具有定制的行為,而又不必為控件本身書寫任何的代碼。動作選擇器和目標連接被歸檔在nib文件中,並在nib文件被解檔時復活。您也可以通過向控件或它的單元對象發送setTarget:
和setAction:
消息來動態地改變目標和動作。
目標-動作機制經常用於通知定制控制器對象將數據從用戶界面傳遞給模型對象,或者將模型對象的數據顯示出來。Cocoa綁定技術則可以避免這種用法,有關這種技術的更多信息請參見Cocoa綁定編程主題文檔。
Application Kit中的控件和單元並不保持它們的目標。相反,它們維護一個對目標的弱引用。進一步的信息請參見"委托、觀察者、和目標的所有權" 部分。
合成模式
這種模式將互相關聯的對象合成為樹結構,以表現部分-全部的層次結構。它使客戶代碼可以統一地處理單獨的對象和多個對象的合成結果。
視圖層次結構
一個窗口包含的視圖對象(NSView
對象)在內部構成了一個視圖層次結構。層次結構的根是窗口對象(NSWindow
對象)和它的內容視圖。內容視圖就是填充到窗口內容方框中的透明視圖,添加到內容視圖中的視圖都是它的子視圖,而這些子視圖又會成為下一級視圖的父視圖。一個視圖有一個(且只有一個)父視圖,可以有零或多個子視圖。在視覺上,您可以將這個結構理解為包含關系:父視圖包含它的子視圖。圖4-2顯示視圖層次的結構以及在視覺上的關系。
視圖層次是一個結構方面的架構,在圖形描畫和事件處理上都扮演一定的角色。一個視圖有兩個影響圖形操作位置的邊界框,即邊框(frame)和邊界(bound)。邊框是外部邊界,表示視圖在其父視圖坐標系統中的位置,它負責定義視圖的尺寸,並根據視圖邊界對圖形進行裁減。邊界則是內部的邊界框,負責定義視圖對象自身描畫表面的內部坐標系統。
當窗口系統要求一個窗口做好顯示准備時,父視圖會在其子視圖之前被要求進行渲染。當您向一個視圖發送消息時—比如發送一個重畫視圖的消息—該消息就會被傳播到子視圖。因此,您可以將視圖層次結構中的一個分支當成一個統一的視圖來處理。
響應者鏈也把視圖層次用於處理事件和動作消息。請參見責任鏈模式部分中("責任鏈模式")關於響應者鏈的總結描述。
使用和限制
無論以編程的方式,還是通過Interface Builder,當您將一個視圖加入到另一個視圖中時,都需要創建或修改視圖層次結構。Application Kit框架自動處理與視圖層次結構相關聯的所有關系。
裝飾模式
這種模式動態地將額外的責任附加到一個對象上。在進行功能擴展時,裝飾是子類化之外的一種靈活的備選方法。和子類化一樣,采納裝飾模式可以加入新的行為,而又不必修改已有的代碼。裝飾將需要擴展的類的對象進行包裝,實現與該對象相同的接口,並在將任務傳遞給被包裝對象之前或之后加入自己的行為。裝飾模式表達了這樣的設計原則:類應該接納擴展,但避免修改。
一般性的說明
裝飾是用於對象合成的模式。在您自己的代碼中應該鼓勵使用對象的合成(參見"什么時候需要生成子類"部分)。然而,Cocoa自己提供了一些基於這種模式的類和機制(在下面的部分進行討論)。在這些實現中,擴展對象並不完全復制它所包裝的對象的接口,雖然具體實現中可以使用不同的技術來進行接口共享。
Cocoa在實現某些類時用到了裝飾模式,包括NSAttributedString
、NSScrollView
、和NSTableView
。后面兩個類是復合視圖的例子,它們將其它一些視圖類的對象組合在一起,然后協調它們之間的交互。
委托
委托是在宿主對象中嵌入一個指向另一對象(也就是委托對象)的弱引用(一個未保持的插座變量),並不時地向該委托對象發送消息,使其對有關的任務進行輸入的機制。宿主對象一般是一個“復活”的框架對象(比如一個NSWindow
或NSXMLParser
對象),它尋求完成某項工作,但又只能以一般的方式來進行。委托幾乎總是一個定制類的實例,它負責配合宿主對象,在有關任務的特定點(參見圖4-3)上提供與具體程序有關的行為。這樣,委托機制使我們可以對另一個對象的行為進行修改或者擴展,而不需要生成子類。
簡而言之,委托就是一個對象將任務委托給另一個對象,它是面向對象編程的常用技術。然而,Cocoa以獨特的方式實現委托機制。宿主類用非正式協議—即NSObject
中的范疇—來定義委托對象可能選擇實現的接口。委托對象不必象采納正式協議那樣實現所有的協議方法。在向委托對象發送消息之前,宿主對象可以首先確定相應的方法是否實現(通過respondsToSelector:
消息),以避免運行時例外。有關正式協議和非正式協議的更多信息,請參見"協議"部分。
Cocoa框架中的一些類也向它們的數據源發送消息。數據源在各個方面都和委托一樣,除了它的目的是為宿主對象提供數據,以傳遞給瀏覽器、表視圖、或者類似的用戶界面視圖。和委托不同的是,數據源還必須實現某些協議方法。
委托不是裝飾模式的嚴格實現。宿主(委托)對象並沒有包裝它希望擴展的類的實例。相反,委托是對委托框架類的行為進行特殊化。除了框架類聲明的委托方法之外,它們也沒有公共的接口。
Cocoa中的委托也是模板方法模式("模板方法模式")的一部分。
使用和限制
委托是Cocoa框架中的一種常用的設計。Application Kit框架中的很多類都向它們的委托發送消息,包括NSApplication
、NSWindow
、和NSView
的幾個子類。Foundation框架中的一些類,比如NSXMLParser
和NSStream
,也維護自己的委托。您應該總是使用類的委托機制,而不是生成類的子類,除非委托方法不能完成您的目標。
雖然您可以動態地改變委托,但是同時只能有一個對象可以成為委托。因此,如果您希望當特定的程序事件發生時,有多個對象可以同時得到通知,則不能使用委托。在這種情況下您可以使用通告機制。如果委托對象實現了一或多個框架類聲明的通告方法,就會自動接收到其委托框架對象的通告。請參考觀察者模式( "觀察者模式")中有關通告的討論。
Application Kit框架中的向外委托任務的對象並不保持它們的委托或數據源,而是維護一個弱引用,更多信息請參見"委托、觀察者、和目標的所有權"部分。
范疇
范疇是Objective-C語言的一個特性,用於為一個類增加方法(接口和實現),而不必生成子類。類原始聲明的方法和通過范疇添加的方法在運行時沒有區別—在您的程序的作用范圍內。范疇中的方法成為類類型的一部分,並被所有的子類繼承。
和委托一樣,范疇並沒有嚴格適配裝飾模式。它實現了該模式的目的,但采用不同的實現方式。范疇加入的行為是在編譯時生成的,而不是動態得到的。而且,范疇並沒有封裝被擴展的類的實例。
使用和限制
Cocoa框架中定義了很多范疇,大多數都是非正式協議(在"協議"部分中進行總結)。它們通常使用范疇來對相關的方法進行分組。您也可以在代碼中實現范疇,以在不生成子類的情況下對類進行擴展,或者對相關的方法進行分組。但是您需要注意如下兩點:
-
您不能為類添加實例變量。
-
如果您對現有的方法進行重載,則應用程序可能產生預料之外的行為。
表觀模式
這種模式為子系統中的一組接口提供統一的接口。表觀模式定義一個更高級別的接口,通過減少復雜度和隱藏子系統之間的通訊和依賴性,使子系統更加易於使用。
NSImage
NSImage
類為裝載和使用基於位圖(比如JPEG、PNG、或者TIFF格式)或向量(EPS或PDF格式)的圖像提供統一的接口。NSImage
可以為同一個圖像保持多個表示,不同的表示對應於不同類型的NSImageRep
對象。NSImage
可以自動選擇適合於特定數據類型和顯示設備的表示。同時,它隱藏了圖像操作和選擇的細節,使客戶代碼可以交替使用很多不同的表示。
使用和限制
由於NSImage
支持幾種不同的圖像表示,因此某些屬性可能不能適用。舉例來說,您可能不能取得圖像中一個像素的顏色,如果潛在的圖像表示使基於向量且與設備無關的話。
迭代器模式
這種模式提供一種順序訪問聚合對象(也就是一個集合)中的元素,而又不必暴露潛在表示的方法。迭代器模式將訪問和遍歷集合元素的責任從集合對象轉移到迭代器對象。迭代器定義一個訪問集合元素的接口,並對當前元素進行跟蹤。不同的迭代器可以執行不同的遍歷策略。
NSEnumerator
Foundation框架中的NSEnumerator
類實現了迭代器模式。NSEnumerator
抽象類的私有具體子類返回的枚舉器對象可以順序遍歷不同類型的集合—數組、集合、字典(值和鍵)—並將集合中的對象返回給客戶代碼。
NSDirectoryEnumerator
是一個不緊密相關的類,它的實例可以遞歸地枚舉文件系統中目錄的內容。
使用和限制
象NSArray
、NSSet
、和NSDictionary
這樣的集合類都包含相應的方法,可以返回與集合的類型相適用的枚舉器。所有的枚舉器的工作方式都一樣。您可以在循環中向枚舉器發送nextObject
消息,如果該消息返回nil
,而不是集合中的下一個對象,則退出循環。
仲裁者模式
這種模式定義的對象用於封裝一組對象的交互機制。仲裁者模式可以避免對象之間顯式的互相引用,使對象之間的耦合變得寬松,也使您可以獨立地改變它們的交互方式。這些對象也因此可以更具重用性。
仲裁者對象集中了系統中的對象之間的復雜通訊和控制邏輯。這些對象在狀態發生改變時會告訴仲裁者對象,反過來,也對仲裁者對象的請求進行響應。
控制器類
模型-視圖-控制器設計模式為一個面向對象的系統(比如一個應用程序)中的對象分配不同的角色。它們可以是模型對象,包含應用程序的數據及對那些數據進行操作;可以是視圖對象,負責表示數據及響應用戶動作;也可以是控制器對象,負責協調模型和視圖對象。控制器對象適合於仲裁者模式。
在Cocoa中,控制器對象一般有兩個類型:仲裁控制器或者協調控制器。仲裁控制器負責仲裁應用程序中視圖對象和模型對象之間的數據流。仲裁控制器通常是NSController
對象。協調控制器則負責實現應用程序的集中化通訊和控制邏輯,作為框架對象的委托和動作消息的目標。它們通常是NSWindowController
對象或定制NSObject
子類的實例。由於協調控制器高度專用於特定的程序,因此不考慮重用。
Application Kit框架中的NSController
抽象類和它的具體子類是Cocoa綁定技術的一部分,該技術可以自動同步模型對象包含的數據和視圖對象中顯示及編輯的數據。舉例來說,如果用戶在一個文本框中編輯一個字符串,綁定技術會將文本框中的變化(通過仲裁控制器)傳遞給綁定了的模型對象中合適的屬性。編程者需要做的就是正確設計自己的模型對象,並通過Interface Builder在程序的視圖、控制器、和模型對象之間建立綁定關系。
具體的公共控制器類的實例可以在Interface Builder的控件選盤上得到,因此是高度可重用的。它們提供一些服務,比如選擇和占位符值的管理。這些對象執行下面這些特定的功能:
-
NSObjectController
管理一個單獨的模型對象。 -
NSArrayController
管理一個模型對象數組,以及維護一個選擇;還可以在數組中加入或刪除模型對象。 -
NSTreeController
使您可以在一個具有層次的樹結構中添加、刪除、和管理模型對象。 -
NSUserDefaultsController
為預置(用戶缺省值)系統提供一個便利的接口。
使用和限制
您通常可用將NSController
對象用作仲裁控制器,因為這些對象的設計目的是在應用程序的視圖對象和模型對象之間傳遞數據。在使用仲裁控制器時,您通常是從Interface Builder選盤中拖出對象,指定模型對象的屬性鍵,並通過Interface Builder Info 窗口中的Bindings面板建立視圖和模型對象之間的綁定關系。您也可以生成NSController
或其子類的子類,以獲得更具具體行為的子類。
幾乎任何一對對象之間都可以建立綁定關系,只要它們遵循NSKeyValueCoding
和NSKeyValueObserving
這兩個非正式協議。但是,我們推薦您通過仲裁控制器來建立綁定,以得到NSController
及其子類為您提供的各種好處。
協調控制器通過下面的方式集中實現一個應用程序的通訊和控制邏輯:
-
維護指向模型和視圖對象的插座變量(插座變量是指向其它被保持為實例變量的連接或引用)。
-
通過目標-動作機制響應用戶在視圖對象上的操作(參見"目標-動作"部分)。
-
作為委托對象,處理從框架對象發出的消息(參見"委托"部分)。
您通常可以在Interface Builder中建立上述的連接—插座變量、目標-動作、和委托,它將這些連接歸檔到應用程序的nib文件中。
備忘錄模式
這種模式在不破壞封裝的情況下,捕捉和外部化對象的內部狀態,使對象在之后可以回復到該狀態。備忘錄模式使關鍵對象的重要狀態外部化,同時保持對象的內聚性。
歸檔
歸檔將一個程序中的對象以及對象的屬性(包括屬性和關系)存儲到檔案上,使之可以保存到文件系統中,或者在不同的處理器和網絡間傳遞。檔案將程序的對象圖保存為獨立於架構的字節流,對象的標識和對象之間的關系都會被保留。由於對象的類型和它的數據一起被存儲,從歸檔的字節流解碼出來的對象會被正常實例化,實例化所用的類與原來編碼的類相同。
使用和限制
通常情況下,您希望將程序中需要保存狀態的對象歸檔。模型對象幾乎總是屬於這個范疇。您通過編碼將對象寫入到檔案中,而通過解碼將對象從檔案中讀取出來。通過NSCoder
對象可以執行編解碼操作,在編解碼過程中最好使用鍵化的歸檔技術(需要調用NSKeyedArchiver
和NSKeyedUnarchiver
類的方法)。被編解碼的對象必須遵循NSCoding
協議;該協議的方法在歸檔過程中會被調用。
屬性列表的序列化
屬性列表是一個簡單的、具有一定結構的對象圖序列,它僅使用下面這些類的對象:NSDictionary
、NSArray
、NSString
、NSData
、NSDate
、和NSNumber
。這些對象通常也被稱為屬性列表對象。Cocoa中有幾個框架類提供了序列化屬性列表對象,以及定義錄寫對象內容及其層次關系的特殊數據流格式的方法。NSPropertyListSerialization
類就提供了將屬性列表對象序列化為XML或其它優化的二進制格式的類方法。
使用和限制
如果對象圖中包含的是簡單對象,則在捕捉和外部化對象及其狀態時,屬性列表序列化是一種靈活的、可移植的、而又非常適當的工具。然而,這種形式的序列化有它的限制,它不保留對象的全部類標識,而只保留一些一般的類型(數組、字典、字符串、等等)。這樣,從屬性列表恢復出來的對象可能和原來的類不同,特別是當對象的可變性可能發生變化時,這就會帶來問題。屬性列表序列化也不跟蹤在同一對象中被多次引用的對象,這可能導致反向序列化時產生多個實例,而在原來的對象圖中卻只有一個實例。
Core Data
Core Data是一個管理對象圖,並使其留存的框架和架構。正是第二種能力—對象的留存能力—使Core Data成為備忘錄模式的一種適配形式。
在Core Data架構中,中心的對象稱為被管理對象上下文,負責管理應用程序對象圖中的模型對象。在被管理對象上下文下面是該對象圖的持久棧,也就是一個框架對象的集合,負責協調模型對象和外部數據存儲,比如XML文件或關系數據庫。持久棧對象負責建立存儲中的數據和被管理對象上下文中的對象之間的映射關系,在有多個數據存儲的時候,持久棧對象將這些存儲表現為被管理對象上下文中的一個聚合存儲。
Core Data的設計也在很大程度上受到模型-視圖-控制器以及對象建模模式的影響。
使用和限制
Core Data在開發企業級應用程序時特別有用,這些程序需要定義、管理、以及從數據存儲中透明地歸檔和解檔復雜模型對象圖。Xcode開發環境中包含有關的工程模板和設計工具,在創建兩種一般類型的Core Data應用程序(即基於文檔和非基於文檔的類型)時,這些模板和工具可以節省一些編程的工作量。Interface Builder也在其選盤中包含可配置的Core Data框架對象。
進一步閱讀: 在本文檔中,"其它Cocoa架構"部分中包含對Core Data的總結。更多Core Data的信息,請閱讀Core Data編程指南。NSPersistentDocument Core Data教程和底層Core Data教程則一步一步地教您創建基於文檔和非基於文檔的Core Data應用程序的基本流程。
觀察者模式
這種模式定義一種對象間一對多的依賴關系,使得當一個對象的狀態發生變化時,其它具有依賴關系的對象可以自動地被通知和更新。觀察者模式本質上是個發布-定閱模型,主體和觀察者具有寬松的耦合關系。觀察和被觀察對象之間可以進行通訊,而不需要太多地了解對方。
通告
Cocoa的通告機制實現了一對多的消息廣播,其實現方式符合觀察者模式。在這種機制中,程序里的對象將自己或其它對象添加到一或多個通告的觀察者列表中,每個通告由一個全局的字符串(即通告的名稱)標識。希望向其它對象發送通知的對象-也就是被觀察的對象-負責創建一個通告對象,並將它發送到通告中心。通告中心則負責確定通告的觀察者,並通過消息將通告發送給觀察者對象。通告消息激活的方法必須遵循特定的單參數簽名格式,方法的參數就是通告對象,包含通告的名稱、被觀察的對象、以及一個含有補充信息的字典。
通告的發送是一個同步的過程。在通告中心將通告廣播給所有的觀察者之前,發送對象不會再得到程序的控制權。對於異步的處理方式,控制權會在您將通告放入通告隊列中之后立即返回到發送對象,當通告到達隊列的頂部時,通告中心會將它廣播出去。
常規的通告-也就是那些由通告中心廣播的通告-只能在進程內部傳播。如果您希望將通告廣播給其它進程,需要使用分布式通告中心及其相關的API。
使用和限制
使用通告可以有很多原因。例如,借助通告機制,您可以根據程序中其它地方發生的事件改變用戶界面元素顯示信息的方式。或者,您可以用通告來保證文檔中的對象在文檔窗口關閉之前保存自己的狀態。通告的一般目的是將事件通知給其它程序對象,使它們可以做出恰當的反應。
但是,通告的接收對象只能在事件發生之后進行反應,這和委托機制有顯著的不同。被委托的對象有機會拒絕或修改委托對象希望進行的操作。另一方面,觀察者對象不能直接影響一個即將發生的操作。
與通告有關的類有NSNotification
(通告對象)、NSNotificationCenter
(用於發送通告和添加觀察者)、NSNotificationQueue
(負責管理通告隊列)、和NSDistributedNotificationCenter
。很多Cocoa框架都發布和發送通告,其它對象都可以成為這些通告的觀察者。
鍵-值觀察
鍵-值觀察是使對象可以在其它對象的具體屬性發生變化時得到通知的一種機制。它基於名為NSKeyValueObserving
的非正式協議。被觀察的屬性可以是簡單的屬性、一對一的關系、或者一對多的關系。鍵-值觀察在模型-視圖-控制器模式中特別重要,因為它使視圖對象-通過控制器層-可以觀察到模型對象的變化,因此是Cocoa綁定技術的必要組件(參見"控制器類"部分)。
Cocoa為很多NSKeyValueObserving
方法提供了缺省的“自動”實現,使所有遵循該協議的對象都具有屬性-觀察的能力。
使用和限制
鍵-值觀察和通告機制類似,但在一些重要的方面也有不同。在鍵-值觀察中,沒有為所有觀察者提供變化通告的中心對象,而是將變化的通告直接傳遞給觀察對象。還有,鍵-值觀察直接和具體的對象屬性相關聯。而通告機制則更廣泛地關注程序的事件。
參與鍵-值觀察(KVO)的對象必須滿足特定的要求-或者說是遵循KVO,記憶起來更為方便。對於自動觀察來說,這需要滿足鍵-值編碼的要求(也稱為遵循KVC),並使用遵循KVC的方法(也就是存取方法)。鍵-值編碼是一個與自動獲取和設置對象屬性值有關的機制(基於相關的非正式協議)。
您可以禁用自動的觀察者通告,並通過NSKeyValueObserving
非正式協議及相關范疇中的方法實現手工通告,從而對KVO通告進行精化。
代理模式
這種模式為某些對象定義接口,使其充當其它對象的代理或占位對象,目的是進行訪問控制。這種模式可以用於為一個可能是遠程的、創建起來開銷很大的、或者需要保證安全的對象創建代表對象,並在代表對象中為其提供訪問控制的場合。它在結構上和裝飾模式類似,但服務於不同的目的;裝飾對象的目的是為另一個對象添加行為,而代理對象則是進行訪問控制。
NSProxy
NSProxy
類定義的接口適用於為其它的、甚至是尚未存在的對象充當代理或占位符的對象。代理對象通常將消息轉發給自己代表的對象,但是也可以通過裝載其代表的對象或對自身進行改造來對消息進行響應。雖然NSProxy
是一個抽象類,但它實現了NSObject
協議及其它根對象應該具有的基本方法,它實際上是和NSObject類一樣,是一個類層次的根類。
NSProxy
的具體子類可以完成代理模式規定的目標,比如對開銷大的對象進行遲緩實例化,或者實現安全守衛對象等。NSDistantObject
就是Foundation框架中的NSProxy
類的一個具體子類,負責為透明的分布式消息傳遞實現遠程的代理對象。NSDistantObject
對象是分布式對象架構的一部分。這些對象充當其它進程或線程中的對象的代理,幫助實現那些線程或進程中的對象之間的通訊。
NSInvocation
對象采納了命令模式,也是分布式對象架構的一部分。
使用和限制
Cocoa只在分布式對象中使用NSProxy
對象。NSProxy
對象是NSDistantObject
和NSProtocolChecker
兩個具體子類的特殊實例。分布式對象不僅可以用於進程間(在同一台或不同的計算機上都可以)的消息傳遞,還可以用於實現分布式計算或並行處理。如果您希望將代理對象用於其它目的,比如昂貴資源的創建或者安全,則需要實現您自己的NSProxy
具體子類。
單件模式
這種模式確保一個類只有一個實例,並提供一個全局的指針作為訪問通道。該類需要跟蹤單一的實例,並確保沒有其它實例被創建。單件類適合於需要通過單個對象訪問全局資源的場合。
框架類
有幾個Cocoa框架類采用單件模式,包括NSFileManager
、NSWorkspace
、和NSApplication
類。這些類在一個進程中只能有一個實例。當客戶代碼向該類請求一個實例時,得到的是一個共享的實例,該實例在首次請求的時候被創建。
使用和限制
使用由單件類返回的共享實例和和使用非單件類的實例沒有什么不同,只是您不能對該實例進行拷貝、保持、和釋放(相關的方法被重新實現為空操作)。您在條件許可的情況下也可以創建自己的單件類。
模板方法模式
這種模式為某個操作中的算法定義框架,並將算法中的某些步驟推遲到子類實現。模板方法模式使子類可以重定義一個算法中的特定步驟,而不需要改變算法的結構。
可重載的框架方法
模板方法模式是Cocoa的基本設計,事實上也是一般的面向對象框架的基本設計。Cocoa中的模式使一個程序的定制組件可以將自己掛到算法上,但何時和如何使用這些定制組件,由框架組件來決定。Cocoa類的編程接口通常包括一些需要被子類重載的方法。在運行環境中,框架會在自己所執行的任務過程中的某些點調用這些所謂的一般方法。一般方法為定制代碼提供一個結構,目的是為當前正在執行且由框架類負責協調的任務加入具體程序的的行為和數據。
使用和限制
為了使用Cocoa采納的模板方法模式,您必須創建一個子類,並重載有關的方法。框架會調用這些方法,並將具體應用程序的信息輸入到正在執行的算法中。如果您正在編寫自己的框架,則可能應該將這個模式包含到您的設計中。
文檔架構
Cocoa的文檔架構就是一個特別的(也是重要的)、采納模板方法模式進行框架方法重載的設計實例。幾乎所有創建和管理多個文檔、而且每個文檔有各自顯示窗口的Cocoa應用程序都基於文檔架構。在這個架構中,有三個互相協作的框架類:NSDocument
、NSWindowController
、和NSDocumentController
。NSDocument
對象管理模型對象,代表文檔的數據,它會根據用戶的請求將數據寫到文件中,或者重新裝載數據並用這些數據重建模型對象;NSWindowController
對象管理負責管理特定文檔的用戶界面;基於文檔的應用程序的NSDocumentController
對象管理負責跟蹤和管理所有打開的文檔,或者協調應用程序的活動。在運行時,這些對象從Application Kit接收要求執行具體操作的消息。應用程序開發者必須重載很多由這些消息激活的方法,以便添加應用程序的具體行為。
Cocoa文檔架構的設計在很大程度上也受到模型-視圖-控制器模式的影響。
使用和限制
您可以在Xcode的新建工程(New Project)助手中選擇基於文檔的Cocoa應用程序(Cocoa Document-based Application)模板,創建基於文檔的Cocoa應用程序工程。然后,您需要實現一個NSDocument
的定制子類,選擇實現NSWindowController
和NSDocumentController
的定制子類。Application Kit為您提供很多應用程序需要的文檔管理邏輯。