標籤:des style http io ar os 使用 sp strong
本文轉載至 http://www.cocoachina.com/ios/20141124/10296.html
相信大家都瞭解GoF的《Design Patterns》中提到的23種設計模式,其中將常見的設計模式分為三大類:建立型模式、行為型模式、結構型模式。而在《Clean Code》中也提到建造酒店的例子,系統中對象的構建和使用應當分離開,那麼應該怎麼構建對象更加整潔和符合使用情境就很重要。
在iOS的系統類別庫中也有一種方式使得開發人員不必關注類中具體的儲存實現,但可以根據不同需求情境建立出合適的對象來。比如Foudation中的NSArray、UIkit中的UIButton。
本文結合幾種工廠設計模式的原理,對Objective-C類族的概念做一下簡要的整理。
0. 三種工廠
其實除了《Design Patterns》中提到的Factory Method和Abstract Factory,常提到的還有另一種原廠模式Simple Factory。
在簡單工廠中,產品有一個統一的interface,而可以有不同implementation,同時有一個(通常是僅有一個)工廠對象。需要產品的時候,工廠會根據已有條件做switch,選擇一種產品實現,構建一個執行個體出來。這種設計將產品的抽象和實現分離開來,可以針對統一的產品介面進行通訊,而不必特意關注具體實現的不同。對於產品類別的擴充也是可以的,但每增加一個產品,都需要修改工廠的邏輯,有一定維護成本。
Factory 方法更進一步,將工廠也抽象出來,進行介面、實現分離。這樣具體工廠和具體產品可以對應著同時擴充,而不需要修改現有邏輯。當然,使用者也許在不同情境要在一定程度上自己對應的工廠選擇(這個總要有人知道,不可避免)。
抽象工廠相對於Factory 方法主要是對整個產品類的體系進行了橫向擴充,構成一個更為完整的系統。
1. Objective-C的類族(Class Cluster)
做iOS開發的朋友們一定用過NSNumber的numberWith…方法。但大家有可能都不知道NSNumber這樣的方法調用返回的不是NSNumber類本身的對象,這正是Objective-C類族的微妙之處。
如所示,Number的概念很大。而實際上NSNumber實際上是有很多隱藏的子類的,而我們通過NSNumber的numberWith…方法得到的對象正是其子類的對象,但對於使用者幾乎可以不必知道這一點,只要知道它是一個NSNumber對象就OK了。
“Simple Concept and Simple Interface”,這正是蘋果設計類族的初衷,也是類族的優點所在。可以想象我們要用整數作為參數拿到一個NSNumber對象和一個布爾值參數拿到的NSNumber對象是不同的,這略微有些類似於switch邏輯(雖然是通過不同的方法),根據不同的條件提供不同的子類對象,而這一切都集中聲明在公用介面類NSNumber中。我們很容易聯想到上面提到的Simple Factory(簡單工廠)設計模式。
沒錯,與簡單工廠類似,類族的一個缺點也顯現出來,那就是已有的類族不好擴充。比如你想NSNumber再多支援一種情況,這個恐怕很難。好在這些系統的類庫已經將大部分可能都做進去了,考慮得比較完善,通常你只是去用就可以了。
2. 類族的子類擴充
瞭解了類族的概念,我們在實際開發當中也可以採用其方式,利用其優點。上面提到對已有類族進行子類擴充是很難的,但這不代表NSNumber、NSArray等類就沒法繼承了。他們還是可以有自訂的子類的。
既然要做類族的子類,就要做到:
· 以公用“抽象”類為父類,比如NSNumber、NSArray等,而非其子類
· 提供自訂儲存
· 重寫(覆蓋)父類所有初始化方法
· 重寫父類中“原始”方法
其中第二點最重要,系統的類族通常在父類中只是提供了各種方法聲明,而自身並不提供儲存,所以要自訂子類一定要提供自己的儲存,一般情況下這也是自訂子類的意義所在。
重寫初始化方法,要遵從Objective-C初始化鏈的規範。
而重寫“原始”方法,這個要說一下。按照蘋果的文檔,和指定初始化方法形式類似,這些類裡面的眾多方法可以分為兩類,“原始”方法和“衍生”方法。“原始”方法定義了這個類及對象的最基本行為,而“衍生”方法則基於這些“原始”方法進行更複雜邏輯的封裝。所以,重寫了“原始”方法,“衍生”方法也自然效果就不同了。
除了自訂子類外,蘋果官方更建議開發人員用組合的方式對類族類進行封裝。
3. 對象所屬類的判斷
有人會問,如果我沒有特殊需求,不需要寫NSArray、NSNumber的子類,是不是瞭解類族就沒有多大意義了。這裡記一下,通過瞭解類族概念,我們至少知道了,通過NSNumber得到的對象,不一定是(基本上就不會是)NSNumber類本身的對象。
可以實驗下,通過[NSNumber numberWithInt:2]和[NSNumber numberWithBool:YES]得到的對象對應的類,一個是__NSCFNumber,另一個是__NSCFBoolean。
那麼,如下這樣的判斷就不行了:
1234 |
id maybeAnArray = /* ... */ ; if ([maybeAnArray class] == [NSArray class]) { // Will never be hit } |
需要在適當的情況下選擇使用isMemberOfClass和isKindOfClass。
本文到這就整理這麼多,更多內容可參看:
ClassClusters
《Effective Objective-C》第9條
Objective-C類族和原廠模式