工廠模式三部曲 - 簡單工廠模式

該文章屬於<簡書 — 劉小壯>原創,轉載請註明:

<簡書 — 劉小壯> http://www.jianshu.com/p/a523144d8d7agit


以前寫過一篇關於簡單工廠模式的博客,後來再看感受以前寫的不太好,並且不夠詳細。這兩天正好有時間,打算把以前簡單工廠模式的文章重寫,此次要寫關於工廠模式的一系列文章,而不僅是一篇文章。github

這系列文章將會從淺入深,講述三種工廠模式的設計,分別是:簡單工廠模式、工廠方法模式、抽象工廠模式。因爲__反射機制能夠簡化工廠模式__,因此這系列文章將會給出沒有使用反射機制,和使用了反射機制的兩種實現代碼。算法

本人理解可能不夠深入,這一系列文章中存在的問題,歡迎你們提出,謝謝!編程


博客配圖

什麼是簡單工廠模式?

簡單工廠模式中定義一個抽象類,抽象類中聲明公共的特徵及屬性,抽象子類繼承自抽象類,去實現具體的操做。工廠類根據外界需求,在工廠類中建立對應的抽象子類實例並傳給外界,而對象的建立是由外界決定的。外界只須要知道抽象子類對應的參數便可,而不須要知道抽象子類的建立過程,在外界使用時甚至不用引入抽象子類設計模式

簡單工廠模式將抽象子類的建立,和關於抽象子類相關的業務邏輯分離,下降對象間的耦合度。因爲工廠類只是爲外界建立對象,因此並不須要實例化工廠類對象,只須要爲外界提供類方法便可。外界須要什麼類型的抽象子類,只須要傳遞對應的參數便可。外界不須要知道具體的抽象子類,只須要使用抽象類便可。工具

簡單工廠模式主要包含三部分:
  • 工廠類:根據外界的需求,決定建立並返回哪一個具體的抽象子類。
  • 抽象類:定義抽象子類所需的屬性和方法,子類經過繼承自抽象類得到這些方法。
  • 抽象子類:繼承自抽象類,是具體操做的實現者,根據需求重寫父類繼承過來的方法。

業務場景

簡單工廠模式主要適用於抽象子類的業務邏輯相同,但具體實現不一樣的狀況。不一樣的操做子類執行一樣的方法,最後的結果倒是不一樣的,這也是多態的一種表現方式。學習

這裏用一個簡單的加減乘除的基礎運算例子看成需求,下面的__UML__類圖和代碼都會依據這個場景來實現。假設如今須要實現一個簡單的加減乘除運算,這些運算具體操做都是相似的,都有兩個被操做的值和一個運算方法,只是運算符不一樣,這種狀況就適合用簡單工廠模式。優化

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

相關文章
相關標籤/搜索