類簇可以說是Objective-C
語(yǔ)言中比較重要的設(shè)計(jì)四苇,Apple
在官方文檔中用一篇文章來介紹這個(gè)概念方咆,盡管文章點(diǎn)到為止,并沒有深入到內(nèi)部機(jī)制峻呛,但是也用了詳細(xì)的例子來說明類簇的設(shè)計(jì)是多么優(yōu)秀。Apple
在文檔中稱類簇是基于抽象工廠模式來設(shè)計(jì)的钩述,如果你對(duì)抽象工廠的定義不清晰,可能會(huì)問职恳,抽象工廠是什么?如果你對(duì)抽象工廠的定義清晰放钦,有可能會(huì)問恭金,類簇的設(shè)計(jì)真的是基于抽象工廠模式嗎?本文站在設(shè)計(jì)模式的源頭横腿,對(duì)類簇進(jìn)行挖掘解讀斤寂。
先別急揪惦,我的文章需要延續(xù)一貫的個(gè)人風(fēng)格,有足夠的準(zhǔn)備溪猿,我們先來理解設(shè)計(jì)模式中的工廠模式
工廠模式
工廠模式一共有三種,簡(jiǎn)單工廠模式再愈,工廠方法模式,抽象工廠模式。接下來我們依次通過圖文并茂的描述進(jìn)行理解
簡(jiǎn)單工廠模式
先來看看簡(jiǎn)單工廠的UML
圖(回歸經(jīng)典垂睬,用的Java
語(yǔ)言)
我們將簡(jiǎn)單工廠模式分為幾個(gè)部分來解讀:
-
產(chǎn)品:
- 定義了一個(gè)抽象的產(chǎn)品類
- 有兩個(gè)具體產(chǎn)品類繼承了該抽象類
-
工廠:
-
creatProduct(String):Product
方法:參數(shù)為String
類型,返回值為Product
類型 - 偽代碼實(shí)現(xiàn):
if(type == "A") { return new ProductA(); } else { return new ProductB(); }
-
-
使用:
- 偽代碼示例:
Product product = factory.creatProduct("A"); product.operation();
- 偽代碼示例:
可以發(fā)現(xiàn)钳枕,主要的邏輯代碼寫在了工廠的方法中赏壹,那么如果產(chǎn)品種類增多,我們就需要去修改工廠的方法蝌借。回憶一下設(shè)計(jì)模式六大原則之一的開放-封閉原則自晰,開放指的是面向擴(kuò)展開放,封閉指的是面向修改封閉酬荞。再回到簡(jiǎn)單工廠模式來看,是不是違背了原則呢混巧。所以在實(shí)際應(yīng)用中勤揩,正如它的名字,它僅僅在一些簡(jiǎn)單場(chǎng)景使用陨亡。
工廠方法模式
同樣地缠犀,我們先來看看工廠方法模式的UML
圖
同樣地聪舒,將其分為幾個(gè)部分來解讀:
-
產(chǎn)品:
- 定義了一個(gè)抽象的產(chǎn)品類
- 有兩個(gè)具體產(chǎn)品類繼承了該抽象類
-
工廠:
- 定義了一個(gè)抽象的工廠類
- 有兩個(gè)具體工廠類繼承了該抽象類
-
FactoryA
類中的+ creatProduct():Product
方法偽代碼:return new ProductA();
-
FactoryB
類中的creatProduct():Product
方法偽代碼:return new ProductB();
-
使用:
- 偽代碼演示:
// 創(chuàng)建產(chǎn)品A Factory factoryA = new FactoryA(); Product productA = factoryA.creatProduct(); // 創(chuàng)建產(chǎn)品B Factory factoryB = new FactoryB(); Product productB = factoryB.creatProduct();
- 偽代碼演示:
看完了簡(jiǎn)單工廠模式和工廠方法模式,通過對(duì)比可以發(fā)現(xiàn)箱残,工廠方法模式在簡(jiǎn)單工廠模式的基礎(chǔ)之上,踐行了開放-封閉原則被辑,創(chuàng)建產(chǎn)品不再通過傳入?yún)?shù)判斷應(yīng)該生成哪個(gè)具體產(chǎn)品,而是將工廠創(chuàng)建產(chǎn)品的任務(wù)放在了子類去做盼理。當(dāng)增加了新的產(chǎn)品時(shí),我們只需要?jiǎng)?chuàng)建新的子類宏怔,實(shí)現(xiàn)方法,不需要對(duì)抽象類進(jìn)行修改鸽粉。
抽象工廠模式
老樣子,來看看抽象工廠的UML
圖
分成三部分來解讀抽象工廠模式:
-
產(chǎn)品:
- 有兩個(gè)抽象的產(chǎn)品類
- 有兩個(gè)具體產(chǎn)品類分別繼承了各自的抽象類
-
工廠:
- 定義了一個(gè)抽象的工廠類
- 有兩個(gè)具體工廠類繼承了該抽象類
- 創(chuàng)建產(chǎn)品的方法触机,以
Factory1
類示例:-
creatProductA():ProductA
方法的偽代碼:return new ProductA1();
-
creatProductB():ProductB
方法的偽代碼:return new ProductB1();
-
-
使用:
- 偽代碼演示:
// 型號(hào)為1的產(chǎn)品 Factory factory1 = new Factory1(); ProductA productA1 = factory1.creatProductA(); ProductB productB1 = factory1.creatProductB(); // 型號(hào)為2的產(chǎn)品 Factory factory2 = new Factory2(); ProductA productA2 = factory2.creatProductA(); ProductB productB2 = factory2.creatProductB();
- 偽代碼演示:
可以發(fā)現(xiàn)玷或,抽象工廠模式其實(shí)就是在工廠方法模式的基礎(chǔ)上增加了產(chǎn)品的種類,產(chǎn)品的抽象類從一個(gè)變成了多個(gè)椒舵,那么這樣一來,產(chǎn)品就構(gòu)成了一個(gè)體系笔宿。這個(gè)體系有兩個(gè)關(guān)鍵詞,產(chǎn)品簇和產(chǎn)品等級(jí)結(jié)構(gòu)泼橘,為了理解這兩個(gè)關(guān)鍵字迈勋,我們先來看個(gè)實(shí)際的例子:
- 產(chǎn)品等級(jí)結(jié)構(gòu):等級(jí)結(jié)構(gòu)就是繼承結(jié)構(gòu),如上圖中手機(jī)和電腦靡菇,有一個(gè)抽象類米愿,然后具體的產(chǎn)品繼承這個(gè)抽象類
- 產(chǎn)品簇:上圖中iphoneX和MacBook Pro都是產(chǎn)于同一個(gè)公司鼻吮,蘋果公司。而小米MIX和小米筆記本Pro也是產(chǎn)于同一家公司椎木。即產(chǎn)品簇的概念是,產(chǎn)于同一家工廠漱竖,位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中的一組產(chǎn)品。
有了以上的準(zhǔn)備知識(shí)之后馍惹,我們來看看Objective-C
中的類簇設(shè)計(jì)
類簇
在官方文檔中,蘋果僅僅告訴我們類簇是基于抽象工廠模式來設(shè)計(jì)的
Class clusters are based on the Abstract Factory design pattern.
文檔中通過對(duì)比是否使用類簇來設(shè)計(jì)接口万矾,告訴了我們類簇的設(shè)計(jì)師優(yōu)秀的脚仔。它能產(chǎn)生簡(jiǎn)單的概念舆绎,同時(shí)能夠使接口簡(jiǎn)化。那么類簇是什么呢吕朵?
類簇的定義
Class clusters group a number of private concrete subclasses under a public abstract superclass. The grouping of classes in this way simplifies the publicly visible architecture of an object-oriented framework without reducing its functional richness.
蘋果告訴我們,類簇匯集了若干私有具體子類隱藏在共有的抽象父類之下硫嘶,通過這種方式,簡(jiǎn)化了面向?qū)ο罂蚣苤泄部梢姷募軜?gòu)沦疾,同時(shí)并沒有減少框架功能的豐富性。
舉個(gè)例子
來看下NSNumber
哮塞,它是Objective-C
中關(guān)于數(shù)據(jù)類型的封裝類
Users of this hierarchy see only one public class,
Number
, so how is it possible to allocate instances of the proper subclass? The answer is in the way the abstract superclass handles instantiation.
對(duì)于這個(gè)層級(jí)的使用者凳谦,僅僅能看到一個(gè)共有類,那就是NSNumber
尸执,所以要分配內(nèi)存創(chuàng)建合適的子類對(duì)象缓醋,又怎么可能呢绊诲?答案就是這個(gè)抽象父類對(duì)如何分配進(jìn)行了處理。
看到這里驯镊,我心里油然而生一個(gè)大大的問號(hào),這都哪跟哪板惑,說好的抽象工廠呢,連個(gè)工廠都沒有冯乘,產(chǎn)品自己生產(chǎn)了自己。如果大家也有這個(gè)問題裆馒,那么帶著這個(gè)問題,繼續(xù)往下看
類簇的使用
官方文檔中舉例:
NSNumber *aChar = [NSNumber numberWithChar:’a’];
NSNumber *anInt = [NSNumber numberWithInt:1];
NSNumber *aFloat = [NSNumber numberWithFloat:1.0];
NSNumber *aDouble = [NSNumber numberWithDouble:1.0];
The abstract superclass in a class cluster must declare methods for creating instances of its private subclasses. It’s the superclass’s responsibility to dispense an object of the proper subclass based on the creation method that you invoke—you don’t, and can’t, choose the class of the instance.
抽象父類務(wù)必需要定義創(chuàng)建私有子類的方法翔横,這是抽象父類的職責(zé)。抽象父類需要基于大家調(diào)用的創(chuàng)建方法去分配內(nèi)存到合適的子類禾唁,同時(shí)使用者不需要也不能選擇對(duì)象的類型,統(tǒng)一使用NSNumber
來管理荡短。
到這里哆键,官方介紹介紹,疑惑并沒有解決籍嘹,大家可以能有這種感覺,除了沒有工廠之外辱士,看起來就和簡(jiǎn)單工廠模式沒兩樣。
我們應(yīng)該如何理解识补?
首先,我們嘗試著先找到工廠在哪里,官方文檔中舉例是通過類方法的祝辣,可以隱藏了一些細(xì)節(jié),那么我們換做對(duì)象方法來試試
id number_alloc = [NSNumber alloc];
id number_init_int = [number_alloc initWithInt:3];
id number_init_bool = [number_alloc initWithBool:YES];
id number_init_char = [number_alloc initWithChar:'c'];
id number_init_float = [number_alloc initWithFloat:1.1];
再來看看類型
(lldb) p number_alloc
(NSPlaceholderNumber *) $0 = 0x0000600000017e30
(lldb) p number_init_int
(__NSCFNumber *) $1 = 0xb000000000000032 (int)3
(lldb) p number_init_bool
(__NSCFBoolean *) $2 = 0x00000001038815e0
(lldb) p number_init_char
(__NSCFNumber *) $3 = 0xb000000000000630 (char)99
(lldb) p number_init_float
(__NSCFNumber *) $4 = 0x00006080000323e0 (float)1.100000
看來是有些收獲的名惩,在alloc
的后,得到的對(duì)象類型是NSPlaceholderNumber
(回想之前解讀官方文檔時(shí)孕荠,蘋果多次提到了allocate(分配))接著進(jìn)行了init
,得到的結(jié)果主要有兩種類型弯予,__NSCFNumber
和__NSCFBoolean
。那么初步的UML
架構(gòu)就可以建立了:
是簡(jiǎn)單工廠嗎锈嫩?
從這個(gè)UML
圖來看艳悔,貌似和簡(jiǎn)單工廠模式差不多呢人柿,但是細(xì)心觀察,這里并沒有像簡(jiǎn)單工廠模式一樣違背了開放-封閉原則揭绑。在工廠類NSPlaceholderNumber
中当犯,提供了創(chuàng)建不種類產(chǎn)品的方法瑟捣。這點(diǎn)和抽象工廠很像。那和抽象工廠不一樣的地方呢蝶柿?
和抽象工廠的對(duì)比
仔細(xì)地想下丈钙,NSNumber
的UML
架構(gòu)就仿佛是將抽象工廠模式中以工廠為單位分割出來的小單位〕猓回到小米和蘋果的那個(gè)例子,就好像其中的一個(gè)產(chǎn)品簇星岗,例如只有蘋果公司以及它的iphoneX和MacBook Pro。嘿嘿俏橘,看到這里,NSNumber
的抽象工廠設(shè)計(jì)的神秘面紗悄悄地揭開了。
為何這么設(shè)計(jì)磷蜀?
剛剛提到了,NSNumber
目前只是產(chǎn)品簇中的一條產(chǎn)品線褐隆。那么如果以后產(chǎn)生了其他類型的產(chǎn)品線,蘋果不需要修改之前的代碼庶弃,只需要添加另外的工廠和另外的產(chǎn)品線德澈,將開放-封閉原則發(fā)揮地玲離盡致。
參考文獻(xiàn)
- 官方文檔:Class Cluster
- 《head first design patterns》