1. 程式人生 > >iOS 三種工廠模式(簡單工廠模式、工廠模式、抽象工廠模式)

iOS 三種工廠模式(簡單工廠模式、工廠模式、抽象工廠模式)

原文轉載

說明:在我理解中,這裡所謂的抽象,其實就是制定某一個介面規範。具體體現在程式碼裡,就是設定一個基類,基類遵守某一套介面規範,但是其具體的實現則通過子類化來完成。當然,這裡我們也可以使用協議來規範這套介面規範。相對的,具體就是具有對抽象做子類化之後產生的實體。

1. 簡單工廠模式

如何理解簡單工廠,工廠方法, 抽象工廠三種設計模式?

簡單工廠的生活場景,賣早點的小攤販,他給你提供包子,饅頭,地溝油烙的煎餅等,小販是一個工廠,它生產包子,饅頭,地溝油烙的煎餅。該場景對應的UML圖如下所示:

圖1

簡單工廠模式的參與者:

工廠(Factory)角色:接受客戶端的請求,通過請求負責建立相應的產品物件。
抽象產品(Abstract Product)角色:

是工廠模式所建立物件的父類或是共同擁有的介面。可是抽象類或介面。
具體產品(ConcreteProduct)物件:工廠模式所建立的物件都是這個角色的例項。

簡單工廠模式的演變:

  • 當系統中只有唯一的產品時,可以省略抽象產品,如圖1所示。這樣,工廠角色與具體產品可以合併。

簡單工廠模式的優缺點:

  • 工廠類含有必要的建立何種產品的邏輯,這樣客戶端只需要請求需要的產品,而不需要理會產品的實現細節。
  • 工廠類只有一個,它集中了所有產品建立的邏輯,它將是整個系統的瓶頸,同時造成系統難以拓展。
  • 簡單工廠模式通常使用靜態工廠方法,這使得工廠類無法由子類繼承,這使得工廠角色無法形成基於繼承的等級結構。

下面編寫了一個賣早餐的例子來說明:

SFFactory.h 作為工廠的角色

#import "SFOperation.h"
typedef NS_ENUM(NSInteger, SFFactoryProductType){
    SFFactoryProductTypeMantou,
    SFFactoryProductTypeYoutiao,
};
@interface SFFactory : NSObject
//工廠(Factory)角色:接受客戶端的請求,通過請求負責建立相應的產品物件。
+ (SFOperation *)operationBreakfast:(SFFactoryProductType )breakfastType;

@end
SFFactory.m

#import "SFOperationMantou.h"
#import "SFOperationYoutiao.h"
@implementation SFFactory
+ (SFOperation *)operationBreakfast:(SFFactoryProductType)breakfastType
{
    //通過列舉返回不同的產品
    SFOperation *operation;
    switch (breakfastType) {
        case SFFactoryProductTypeMantou:
            operation = [[SFOperationMantou alloc] init];
            break;
        case SFFactoryProductTypeYoutiao:
            operation = [[SFOperationYoutiao alloc] init];
            break;
        default:
            return nil;
            break;
    }
    return operation;
}

@end
SFOperation.h 抽象產品角色,通過子類化它來獲得具體的角色

@interface SFOperation : NSObject
@property (nonatomic, copy, readonly)NSString *product;
- (void)productBreakfast; 
@end

SFOperation.m

@implementation SFOperation
- (void)productBreakfast{    
}
@end
SFOperationMantou.h  具體產品角色,SFOperation的子類化,實際工廠返回的可能就是它,當然,也可能是油條、豆漿等
 
@interface SFOperationMantou : SFOperation
- (void)productBreakfast;
@end
SFOperationMantou.m

@synthesize product = _product;
- (void)productBreakfast{
    _product = @"饅頭";
}

最後是main函式的呼叫

SFOperation *sfOperation = [SFFactory operationBreakfast:SFFactoryProductTypeMantou];
[sfOperation productBreakfast];
NSLog(@"生產產品:%@",sfOperation.product);

SFFactory作為一個工廠,裡面包含了建立產品的邏輯,這裡使用一個列舉來控制是建立油條還是饅頭的具體產品。但是這裡的問題就是,如果產品類的邏輯非常複雜,那麼這個共產類會複雜的不可控。當然,外部呼叫的話,根本無需關心內部如何建立具體的產品,因為抽象產品類SFOperation已經制定了一套介面規範。

 簡潔來說:抽象工廠直接 -- (建立) --> 具體產品  (這裡沒有具體工廠的概念 但是有抽象產品的概念)

2. 工廠方法模式

工廠方法使用OOP的多型性,將工廠和產品都抽象出一個基類,在基類中定義統一的介面,然後在具體的工廠中建立具體的產品。工廠方法的生活場景,聯合利華要生產“夏士蓮”和“清揚”兩款洗髮水,它會建一個生產“夏士蓮”的工廠和一個生產“清揚”的工廠。

圖2

工廠方法模式中的參與者:

抽象工廠角色:與應用程式無關,任何在模式中建立物件的工廠必須實現這個介面。
具體工廠角色:實現了抽象工廠介面的具體類,含有與引用密切相關的邏輯,並且受到應用程式的呼叫以建立產品物件。
抽象產品角色:工廠方法所建立產品物件的超型別,也就是產品物件的共同父類或共同擁有的介面。
具體產品角色:這個角色實現了抽象產品角色所聲名的介面。工廠方法所建立的每個具體產品物件都是某個具體產品角色的例項。

工廠方法的優缺點:

  • 降低了工廠類的內聚,滿足了類之間的層次關係,又很好的符合了面向物件設計中的單一職責原則,這樣有利於程式的拓展,如圖三所示:

圖3

總結:把“共性”提取出來,根據各自的“個性”建立各自的繼承共性的實現。下面依然使用早餐的例子來說明:

FFactory.h  //抽象工廠角色

@interface FFactory : NSObject
+ (FFOperation *)createOperation;
@end
FFactory.m

@implementation FFactory
+ (FFOperation *)createOperation{
    FFOperation *operation = [[FFOperation alloc] init];
    return operation;
}
@end
FFOperation.h 抽象產品角色

@interface FFOperation : NSObject
@property (nonatomic, copy, readonly)NSString *product;
- (void)productBreakfast;
@end
FFOperation.m

@implementation FFOperation
- (void)productBreakfast{
    
}
@end
FFactoryMantou.h 具體工廠角色

@interface FFactoryMantou : FFactory
@end
FFactoryMantou.m

#import "FFOperationMantou.h"
@implementation FFactoryMantou
+ (FFOperation *)createOperation{
    return [[FFOperationMantou alloc] init];
}
@end
FFOperationMantou.h 具體產品角色

@interface FFOperationMantou : FFOperation
- (void)productBreakfast;
@end
FFOperationMantou.m

@synthesize product = _product;
- (void)productBreakfast{
    _product = @"饅頭";
}

main裡面的呼叫

FFOperation *ffOperation = [FFactoryMantou createOperation];
[ffOperation productBreakfast];
NSLog(@"生產產品:%@",ffOperation.product);

與簡單工廠模式把所有產品都放到SFFactory來獲取相比,工廠模式每次需要一個新的產品,就需要新建一個具體工廠來生成新的產品。當你需要豆漿的時候,只要新建一個繼承與FFactory的工廠類,再建立一個繼承於FFOperation的具體產品就可以建立新的產品了。對原來的程式碼都不需要做任何修改。這就使得產品的拓展變得簡單。

簡潔來說:具體工廠直接 -- (建立) --> 具體產品  (這裡多了一個具體工廠的概念,但是隻是繼承關係,過多的沒有用上)

3. 抽象工廠設計模式

所謂抽象工廠是指一個工廠等級結構可以創建出分屬於不同產品等級結構的一個產品族中的所有物件,以建立Unix控制元件和Windows控制元件為例說明,我們需要一個抽象工廠下面有兩個子工廠,一個叫做UnixFactory,用於生產Unix族控制元件,一個叫做WinFactory,用於生產Win族控制元件。抽象工廠與工廠方法的區別是,工廠方法中的具體工廠一般只生產一個或幾個控制元件物件,而抽象工廠中的具體工廠生產的是一族控制元件物件。如圖4所示。

圖4

抽象工廠中的參與者:

抽象工廠(Abstract Factory)角色:擔任這個角色的是工廠方法模式的核心,它是與應用系統商業邏輯無關的。
具體工廠(Concrete Factory)角色:這個角色直接在客戶端的呼叫下建立產品的例項。這個角色含有選擇合適的產品物件的邏輯,而這個邏輯是與應用系統的商業邏輯緊密相關的。
抽象產品(Abstract Product)角色:擔任這個角色的類是工廠方法模式所建立的物件的父類,或它們共同擁有的介面。
具體產品(Concrete Product)角色:抽象工廠模式所建立的任何產品物件都是某一個具體產品類的例項。這是客戶端最終需要的東西,其內部一定充滿了應用系統的商業邏輯。

抽象工廠的使用場景:

  • 一個系統不應當依賴於產品類例項如何被建立、組合和表達的細節,這對於所有形態的工廠模式都是重要的。
  • 這個系統有多於一個的產品族,而系統只消費其中某一產品族。
    同屬於同一個產品族的產品是在一起使用的,這一約束必須在系統的設計中體現出來。
  • 系統提供一個產品類的庫,所有的產品以同樣的接口出現,從而使客戶端不依賴於實現。

抽象工廠模式與工廠方法模式的區別:

工廠方法模式:每個抽象產品派生多個具體產品類,每個抽象工廠類派生多個具體工廠類,每個具體工廠類負責一個具體產品的例項建立;
抽象工廠模式:每個抽象產品派生多個具體產品類,每個抽象工廠派生多個具體工廠類,每個具體工廠負責多個(一系列)具體產品的例項建立。

一樣的,下面使用早餐店來舉例:

AFFactory.h 抽象工廠角色

#import "AFOperation.h"
@interface AFFactory : NSObject
typedef NS_ENUM(NSInteger, AFFactoryProductType){
    AFFactoryProductTypeMantou,
    AFFactoryProductTypeYoutiao,
};
+ (instancetype)factoryWithType:(AFFactoryProductType)type;
- (AFOperation *)createProduct;
#import "AFFactoryMantou.h"
#import "AFFactoryYoutiao.h"
@implementation AFFactory
+ (instancetype)factoryWithType:(AFFactoryProductType)type
{
    AFFactory *factory;
    switch (type) {
        case AFFactoryProductTypeMantou:
            factory = [[AFFactoryMantou alloc] init];
            break;
        case AFFactoryProductTypeYoutiao:
            factory = [[AFFactoryYoutiao alloc] init];
            break;
        default:
            break;
    }
    return factory;
}
- (AFOperation *)createProduct
{
    return nil;
}
@end
AFOperation.h 抽象產品角色

@interface AFOperation : NSObject
@property (nonatomic, copy, readonly)NSString *product;
- (void)productBreakfast;
@end
@implementation AFOperation
- (void)productBreakfast{
}
@end
AFFactoryMantou.h 具體工廠角色

@interface AFFactoryMantou : AFFactory
@end
AFFactoryMantou.m

#import "AFOperationMantou.h"
@implementation AFFactoryMantou
- (AFOperation *)createProduct{
    return [[AFOperationMantou alloc] init];
}
@end
@interface AFOperationMantou : AFOperation
@end
@implementation AFOperationMantou
@synthesize product = _product;
- (void)productBreakfast{
    _product = @"饅頭";
}
@end

main函式呼叫

AFFactory *afFactory = [AFFactory factoryWithType:AFFactoryProductTypeMantou];
AFOperation *afOperation = [afFactory createProduct];
[afOperation productBreakfast];
NSLog(@"生產產品;%@",afOperation.product);

使用AFFacytory來建立工廠,遮蔽了內部的實現,你可以不用管內部是使用的饅頭工廠還是油條工廠。你需要關注的就是他們公有的介面就行了。抽象工廠的最大好處在於,讓你感覺不到內部差異性。cocoa框架裡到處都是使用了這種設計。比如NSString、NSNumber。(這方面可以查閱cocoa的類簇這個概念來了解相關資料)當然,缺點就是,如果你修改了抽象類的方法,那麼所有的子類都要跟著一起修改。

簡潔來說:抽象工廠直接 -- (建立) --> 具體工廠 --(建立)--> 具體產品 (這裡是上面的更深一步,把抽象工廠的作用變大,多層,在外層呼叫抽象類,根本不用知道里面是什麼)

總結:從簡單工廠模式到工廠模式,再到抽象工廠模式。可以看到整個模式的一步步演進。簡單工廠模式在產品多樣之後,整個工廠將會變得臃腫而難以維護。於是我們將簡單工廠模式中的工程做了抽象處理,這樣每種產品對應一個工廠。這樣無疑會增加程式碼量。但是好處是顯而易見的,單獨讓一個工廠處理一種產品會讓邏輯變得好維護。但是這樣還不夠,因為增加新的品類,就會產生新的類,對於呼叫者來說,處理太多具有相同介面的類顯然是不合算的。於是,我們使用抽象工廠模式來解決這個問題。我們讓抽象工廠內部做一個封裝,用以隱藏真正的具體工廠。這樣,對於呼叫者來說,即時內部增加了新的產品,你也是不知道的。