<簡書 — 劉小壯> http://www.jianshu.com/p/a523144d8d7agit
以前寫過一篇關於簡單工廠模式的博客,後來再看感受以前寫的不太好,並且不夠詳細。這兩天正好有時間,打算把以前簡單工廠模式的文章重寫,此次要寫關於工廠模式的一系列文章,而不僅是一篇文章。github
這系列文章將會從淺入深,講述三種工廠模式的設計,分別是:簡單工廠模式、工廠方法模式、抽象工廠模式。因爲__反射機制能夠簡化工廠模式__,因此這系列文章將會給出沒有使用反射機制,和使用了反射機制的兩種實現代碼。算法
本人理解可能不夠深入,這一系列文章中存在的問題,歡迎你們提出,謝謝!編程
簡單工廠模式中定義一個抽象類,抽象類中聲明公共的特徵及屬性,抽象子類繼承自抽象類,去實現具體的操做。工廠類根據外界需求,在工廠類中建立對應的抽象子類實例並傳給外界,而對象的建立是由外界決定的。外界只須要知道抽象子類對應的參數便可,而不須要知道抽象子類的建立過程,在外界使用時甚至不用引入抽象子類。設計模式
簡單工廠模式將抽象子類的建立,和關於抽象子類相關的業務邏輯分離,下降對象間的耦合度。因爲工廠類只是爲外界建立對象,因此並不須要實例化工廠類對象,只須要爲外界提供類方法便可。外界須要什麼類型的抽象子類,只須要傳遞對應的參數便可。外界不須要知道具體的抽象子類,只須要使用抽象類便可。工具
簡單工廠模式主要適用於抽象子類的業務邏輯相同,但具體實現不一樣的狀況。不一樣的操做子類執行一樣的方法,最後的結果倒是不一樣的,這也是多態的一種表現方式。學習
這裏用一個簡單的加減乘除的基礎運算例子看成需求,下面的__UML__類圖和代碼都會依據這個場景來實現。假設如今須要實現一個簡單的加減乘除運算,這些運算具體操做都是相似的,都有兩個被操做的值和一個運算方法,只是運算符不一樣,這種狀況就適合用簡單工廠模式。優化
根據上面提出的業務場景來畫一張類圖,因爲在__Mac__中沒找到比較好的畫類圖的工具,因此簡單的畫了一下,主要體現具體結構。atom
從上面圖中咱們能夠看出,圖中定義了一個運算抽象類,全部的運算抽象子類繼承自這個運算抽象類。運算抽象類有兩個參與運算的屬性,經過調用getResult
方法來獲取這兩個值最後運算的結果,調用方式都同樣,只是最後的結果不一樣。抽象類並不參與運算,運算的結果經過運算抽象子類重載getResult
方法去實現。.net
上圖中還定義了一個簡單工廠類,這個簡單工廠類就是用於實現運算抽象子類實例化的邏輯,經過外界傳進來的type
參數,並將實例完成的運算操做類返回。
[@interface](https://my.oschina.net/u/996807) Operation : NSObject [@property](https://my.oschina.net/property) (nonatomic, assign) CGFloat numberOne; [@property](https://my.oschina.net/property) (nonatomic, assign) CGFloat numberTwo; - (CGFloat)getResult; [@end](https://my.oschina.net/u/567204) @implementation Operation - (CGFloat)getResult { return 0; } @end
@interface OperationAdd : Operation @end @implementation OperationAdd - (CGFloat)getResult { return self.numberOne + self.numberTwo; } @end @interface OperationSub : Operation @end @implementation OperationSub - (CGFloat)getResult { return self.numberOne - self.numberTwo; } @end @interface OperationMul : Operation @end @implementation OperationMul - (CGFloat)getResult { return self.numberOne * self.numberTwo; } @end @interface OperationDiv : Operation @end @implementation OperationDiv - (CGFloat)getResult { if (self.numberTwo == 0) { NSLog(@"除數不能爲零"); return 0; } else { return self.numberOne / self.numberTwo; } } @end
static NSString *kOperationAdd = @"OperationAdd"; static NSString *kOperationSub = @"OperationSub"; static NSString *kOperationMul = @"OperationMul"; static NSString *kOperationDiv = @"OperationDiv";
@interface OperationFactory : NSObject + (Operation *)CreateOperationWithType:(NSString *)type; @end @implementation OperationFactory + (Operation *)CreateOperationWithType:(NSString *)type { if ([kOperationAdd isEqualToString:type]) { return [OperationAdd new]; } else if ([kOperationSub isEqualToString:type]) { return [OperationSub new]; } else if ([kOperationMul isEqualToString:type]) { return [OperationMul new]; } else if ([kOperationDiv isEqualToString:type]) { return [OperationDiv new]; } return nil; } @end
- (void)viewDidLoad { Operation *oper = [OperationFactory CreateOperationWithType:kOperationAdd]; oper.numberOne = 13; oper.numberTwo = 24; NSLog(@"result : %f", [oper getResult]); }
到目前爲止簡單工廠模式的代碼就寫完了,能夠看到外界想進行什麼類型的運算,只須要將傳入的運算類型參數改一下便可,工廠類就會實例化不一樣的抽象子類進行運算。可是這種工廠類的設計,有一個很大的問題,就在於每次增長或刪除某個算法時,都須要對工廠類進行修改,這是不符合__開放封閉原則__的。對於這個問題,咱們後面會經過__反射機制__來進行處理。
工廠模式也是對__面向對象編程__三大特性之一的__多態__的一個很好的表述,下面先簡單的介紹一下多態的特性。
__面向對象編程三大特性__之一就有多態,多態是指在程序運行時,相同的消息可能會發給繼承自同一個父類的不一樣子類型的對象,雖然是同一個方法,可是運行時系統會根據當前對象所屬的子類型做出不一樣的響應。
面向對象三大特性中,繼承和封裝都是爲了代碼重用,繼承能夠繼承自父類的特徵和屬性,封裝能夠將實現細節封裝,外界調用實現某些功能。而多態則是爲了接口重用。
多個子類繼承同一個父類,就會具備和父類相同的行爲和特徵,子類能夠對父類的方法進行重寫,因此可能同一個方法每一個子類的實現都不一樣。經過父類指針指向任意子類對象並調用相同方法,可能會獲得不一樣的結果。
簡單的說就是系統容許將當前類的指針,指向任何繼承自當前類的子類,而且不會報錯。因爲子類繼承自父類,因此和父類有相同的特徵(方法)。當前類的指針向指向的子類對象發送消息,系統會根據具體的子類對父類方法的實現,做出不一樣的響應。
例以下面這行代碼:
Operation *obj = [OperationFactory CreateOperationWithType:kOperationAdd];
在上面這個例子中,OperationFactory
工廠類將會返回Operation
的子類實例,Operation
的子類分別繼承自同一父類,而且對其getResult
方法進行了重寫。Operation
實例化的obj
指針可能指向任何Operation
的子類,並對其發送getResult
消息。最終的結果會根據obj
指針指向的子類有不一樣的結果,這就是__多態__。
我對多態的瞭解很是淺薄,有不對之處還請多多指出,這裏只是順帶提了一下。
我以前寫過一篇文章,詳細講了一下反射機制,因此這裏不就對反射機制詳細介紹了。
在上面的代碼中,咱們會發現工廠類建立抽象子類的代碼都是相同的,只是建立的具體對象不一樣,並且若是抽象子類不少的話,會有過多的條件語句。編程中這種重複代碼咱們都要將其簡化,否則寫出的代碼就像垃圾代碼同樣,這也是新手和老手的區別之一。
在這裏咱們能夠利用__反射機制__來簡化代碼,根據外面須要的操做子類的類型,反射出具體的類。在上面咱們已經定義了一些NSString
類型的靜態變量,這些靜態變量的值就是反射須要的字符串,外界只須要使用這些靜態變量便可,不用本身手打字符串,也防止了錯誤的發生。修改以後外界不須要發生任何變化,只須要知道這些靜態變量便可,只對工廠類進行修改。
只需將OperationFactory
的建立方法改一下實現,其餘地方不受影響。
+ (Operation *)CreateOperationWithType:(NSString *)type { return [NSClassFromString(type) new]; }
改完以後的代碼很是符合面向對象編程的__開放封閉原則,即當外界需求發生變化時,只對現有代碼進行擴展,不對其原有代碼進行修改__的原則。
如今假設再增長一個其餘運算功能,只須要再建立一個繼承自抽象類的抽象子類,在抽象子類中重寫getResult
方法來實現運算,而且在上面定義的靜態變量中加入一個對應的變量。其餘地方都不會受到影響,這就是一個比較好的__面向對象__的設計。
到此爲止,咱們簡單工廠模式就講完了,後續還有兩篇文章繼續講工廠方法模式和抽象工廠模式的文章,文章中不足之處,但願你們多多提出,謝謝!
前段時間寫了關於工廠模式的系列文章,這系列文章理解起來比較難懂。應廣大讀者的須要,這段時間專門給這系列文章補了Demo
。
Demo
只是來輔助讀者更好的理解文章中的內容,應該博客結合Demo
一塊兒學習,只看Demo
仍是不能理解更深層的原理。Demo
中代碼都會有註釋,各位能夠打斷點跟着Demo
執行流程走一遍,看看各個階段變量的值。
Demo地址:劉小壯的Github