創(chuàng)建型設(shè)計模式在iOS中的實踐
一、單例模式
單例模式的定義與特點
單例(Singleton)模式的定義:指一個類全局只有一個實例,且該類能自行創(chuàng)建這個實例的一種模式。
單例模式有 3 個特點:
- 單例類只有一個實例對象;
- 該單例對象必須由單例類自行創(chuàng)建;
- 單例類對外提供一個訪問該單例的全局訪問點;
單例模式的結(jié)構(gòu)與實現(xiàn)
單例模式是設(shè)計模式中最簡單的模式之一。通常,普通類的構(gòu)造函數(shù)是公有的,外部類可以通過“new 構(gòu)造函數(shù)()”來生成多個實例。但是,如果將類的構(gòu)造函數(shù)設(shè)為私有的,外部類就無法調(diào)用該構(gòu)造函數(shù),也就無法生成多個實例。這時該類自身必須定義一個靜態(tài)私有實例,并向外提供一個靜態(tài)的公有函數(shù)用于創(chuàng)建或獲取該靜態(tài)私有實例。
下面來分析其基本結(jié)構(gòu)和實現(xiàn)方法。
單例模式的結(jié)構(gòu)
單例模式的主要角色如下。
- 單例類:包含一個實例且能自行創(chuàng)建這個實例的類。
- 訪問類:使用單例的類。
單例模式在Objective-C中的設(shè)計
[UIApplication sharedApplication]-
[NSUserDefaults standardUserDefaults];
這是OC里良好設(shè)計的一個具體例子
創(chuàng)建屬于自己的單例
創(chuàng)建的單例應(yīng)該滿足以下要求:
- 全局唯一
- 線程安全
- 性能開銷小
我們將采用GCD來創(chuàng)建單例
.h文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface CLSingleton : NSObject
+ (instancetype)shareSingleton;
@end
NS_ASSUME_NONNULL_END
.m文件
#import "CLSingleton.h"
@interface CLSingleton()
@end
static CLSingleton *singleton = nil;
@implementation CLSingleton
+ (instancetype)shareSingleton
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[CLSingleton alloc] init];
});
return singleton;
}
+ (id)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [super allocWithZone:zone];
});
return singleton;
}
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
return singleton;
}
- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
return singleton;
}
@end
在iOS具體開發(fā)中的作用
- 提供全局的緩存數(shù)據(jù)的保存和訪問入口(如token、username等)
- 提供全局狀態(tài)的監(jiān)聽
- 避免創(chuàng)建大量重復(fù)的對象(如網(wǎng)絡(luò)訪問的管理類)
二、原型模式
原型模式的定義與特點
原型(Prototype)模式的定義如下:用一個已經(jīng)創(chuàng)建的實例作為原型,通過復(fù)制該原型對象來創(chuàng)建一個和原型相同或相似的新對象。在這里,原型實例指定了要創(chuàng)建的對象的種類。用這種方式創(chuàng)建對象非常高效,根本無須知道對象創(chuàng)建的細(xì)節(jié)。
原型模式的結(jié)構(gòu)與實現(xiàn)
模式的結(jié)構(gòu)
原型模式包含以下主要角色。
- 抽象原型類:規(guī)定了具體原型對象必須實現(xiàn)的接口。
- 具體原型類:實現(xiàn)抽象原型類的 clone() 方法,它是可被復(fù)制的對象。
- 訪問類:使用具體原型類中的 clone() 方法來復(fù)制新的對象。
原型模式在Objective-C中的設(shè)計
NSString *str = @"abc";
NSString *astr = [str copy];
@protocol NSCopying
- (id)copyWithZone:(nullable NSZone *)zone;
@end
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(nullable NSZone *)zone;
@end
創(chuàng)建屬于自己的原型
.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface CLPerson : NSObject<NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonnull,nonatomic,copy) NSString *pID;
@property (nonatomic, assign) NSUInteger age;
@end
NS_ASSUME_NONNULL_END
.m
#import "CLPerson.h"
@implementation CLPerson
- (instancetype)init
{
self = [super init];
if (self) {
self.name = @"abc";
self.pID = @"123456";
self.age = 15;
}
return self;
}
- (id)copyWithZone:(NSZone *)zone
{
CLPerson *person = [[[self class] alloc] init];
person.age = self.age;
person.name = self.name;
person.pID = self.pID;
return person;
}
@end
在iOS具體開發(fā)中的作用
- 提供復(fù)雜對象的快速創(chuàng)建(如具有包含多層嵌套對象的屬性的對象的創(chuàng)建)
- 提供向局域外傳遞和使用對象的方式(如
[block copy])
三、工廠模式
模式的定義與特點
工廠方法(FactoryMethod)模式的定義:定義一個創(chuàng)建產(chǎn)品對象的工廠接口,將產(chǎn)品對象的實際創(chuàng)建工作推遲到具體子工廠類當(dāng)中。這滿足創(chuàng)建型模式中所要求的“創(chuàng)建與使用相分離”的特點。
我們把被創(chuàng)建的對象稱為“產(chǎn)品”,把創(chuàng)建產(chǎn)品的對象稱為“工廠”。工廠方法模式的主要優(yōu)點有:
- 用戶只需要知道具體工廠的名稱就可得到所要的產(chǎn)品,無須知道產(chǎn)品的具體創(chuàng)建過程;
- 在系統(tǒng)增加新的產(chǎn)品時只需要添加具體產(chǎn)品類和對應(yīng)的具體工廠類,無須對原工廠進(jìn)行任何修改,滿足開閉原則;
其缺點是:每增加一個產(chǎn)品就要增加一個具體產(chǎn)品類和一個對應(yīng)的具體工廠類,這增加了系統(tǒng)的復(fù)雜度。
模式的結(jié)構(gòu)與實現(xiàn)
在典型的工廠方法模式由工廠和產(chǎn)品2個要素構(gòu)成。根據(jù)單一職責(zé)原則,工廠負(fù)責(zé)創(chuàng)建產(chǎn)品,產(chǎn)品則負(fù)責(zé)被使用。本節(jié)來分析其基本結(jié)構(gòu)和實現(xiàn)方法。
模式的結(jié)構(gòu)
工廠方法模式的主要角色如下。
- 工廠(Factory):提供了創(chuàng)建產(chǎn)品的接口,調(diào)用者通過它訪問具體工廠的工廠方法 newProduct() 來創(chuàng)建產(chǎn)品。
- 產(chǎn)品(Product):實現(xiàn)了抽象產(chǎn)品角色所定義的接口,由具體工廠來創(chuàng)建,它同具體工廠之間一一對應(yīng)。
工廠模式在Objective-C中的設(shè)計
在講工廠模式的代表設(shè)計之前,需要了解下OC中的類簇這個概念
在這里不具體展開,可以參考以下這篇文章:類簇(class cluster)
言歸正傳,我們將通過NSString來看看什么是工廠模式
const char *cr = "bbc";
NSString *astr = [NSString stringWithCString:cr encoding:NSUTF8StringEncoding];
NSLog(@"%@,%@",[@"abc" class],[astr class]);
__NSCFConstantString,NSTaggedPointerString
可以看出來,NSString是一個類簇,這是一個抽象類,它提供了各種子類的創(chuàng)建方法,但外部統(tǒng)一通過NSString來調(diào)用,并且所有的子類都可以使用NSString對外提供的一系列實例方法。
NSLog(@"%@,%@",[str substringFromIndex:1],[astr substringFromIndex:1]);
bc,bc
比較有意思的是,NSString既是工廠string factory,又是產(chǎn)品string的抽象類
關(guān)于工廠的自定義創(chuàng)建,我將在下一節(jié)結(jié)合抽象工廠模式再進(jìn)行具體的演示
四、抽象工廠模式
模式的定義與特點
抽象工廠(AbstractFactory)模式的定義:是一種為訪問類提供一個創(chuàng)建一組相關(guān)或相互依賴對象的接口,且訪問類無須指定所要產(chǎn)品的具體類就能得到同族的不同等級的產(chǎn)品的模式結(jié)構(gòu)。
使用抽象工廠模式一般要滿足以下條件。
- 系統(tǒng)中有多個產(chǎn)品族,每個具體工廠創(chuàng)建同一族但屬于不同等級結(jié)構(gòu)的產(chǎn)品。
- 系統(tǒng)一次只可能消費其中某一族產(chǎn)品,即同族的產(chǎn)品一起使用。
抽象工廠模式除了具有工廠方法模式的優(yōu)點外,其他主要優(yōu)點如下。
- 可以在類的內(nèi)部對產(chǎn)品族中相關(guān)聯(lián)的多等級產(chǎn)品共同管理,而不必專門引入多個新的類來進(jìn)行管理。
- 當(dāng)增加一個新的產(chǎn)品族時不需要修改原代碼,滿足開閉原則。
抽象工廠模式的結(jié)構(gòu)與實現(xiàn)
模式的結(jié)構(gòu)
抽象工廠模式的主要角色如下。
-
抽象工廠(Abstract Factory):提供了創(chuàng)建產(chǎn)品的接口,它包含多個創(chuàng)建產(chǎn)品的方法 newProduct(),可以創(chuàng)建多個不同等級的產(chǎn)品。 -
具體工廠(Concrete Factory):主要是實現(xiàn)抽象工廠中的多個抽象方法,完成具體產(chǎn)品的創(chuàng)建。 -
抽象產(chǎn)品(Product):定義了產(chǎn)品的規(guī)范,描述了產(chǎn)品的主要特性和功能,抽象工廠模式有多個抽象產(chǎn)品。 -
具體產(chǎn)品(ConcreteProduct):實現(xiàn)了抽象產(chǎn)品角色所定義的接口,由具體工廠來創(chuàng)建,它 同具體工廠之間是多對一的關(guān)系。
抽象工廠模式與工廠模式的異同
相同點:
- 都屬于創(chuàng)建型模式
- 都能夠?qū)崿F(xiàn)創(chuàng)建和使用分離
不同點:
- 工廠模式用于創(chuàng)建同一種類的不同等級(或款式)的產(chǎn)品,而抽象工廠模式用于創(chuàng)建不同種類的產(chǎn)品。例如,如果輪胎是一個抽象的產(chǎn)品類,那么自行車輪胎、摩托車輪胎和汽車輪胎就是不同款式的產(chǎn)品;但如果認(rèn)為汽車輪胎是抽象類,那么米其林輪胎和普利司通輪胎就是不同款式的產(chǎn)品。
- 工廠模式可以直接生產(chǎn)產(chǎn)品,抽象工廠模式需要通過抽象工廠先創(chuàng)建工廠,再生產(chǎn)產(chǎn)品,可以簡單理解為,抽象工廠是工廠模式的二次工廠化
抽象工廠模式在Objective-C中的設(shè)計
用工廠模式創(chuàng)建了一個NSNumber
NSNumber *num = [NSNumber numberWithBool:YES];
用抽象工廠模式依次創(chuàng)建了NSString、NSInteger、CGFloat
NSString *str = num.stringValue;
NSInteger i = num.integerValue;
CGFloat f = num.floatValue;
創(chuàng)建屬于自己的工廠、抽象工廠模式
值得說明的是,因為OC的動態(tài)性,可以只用創(chuàng)建一個抽象工廠類,即可實現(xiàn)一套方法創(chuàng)建不同種類不同款式的產(chǎn)品
1、設(shè)定兩類抽象產(chǎn)品(產(chǎn)品族):寶馬汽車類和許可類
2、設(shè)定具體的產(chǎn)品(產(chǎn)品系列):3系,5系,7系,汽車許可
3、設(shè)定抽象工廠
文件目錄如下:

BMWCar.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface BMWCar : NSObject
- (NSString *)carName;
@end
@interface BMWCar3Series : BMWCar
@end
@interface BMWCar5Series : BMWCar
@end
@interface BMWCar7Series : BMWCar
@end
NS_ASSUME_NONNULL_END
BMWCar.m
#import "BMWCar.h"
@implementation BMWCar
- (BOOL)isKindOfClass:(Class)aClass
{
if (!aClass) {
return NO;
}
Class cls = self.class;
while (cls != aClass) {
cls = [cls superclass];
if (cls == NSObject.class) {
return NO;
}
}
return YES;
}
- (NSString *)carName
{
return [[self class] description];
}
@end
@implementation BMWCar3Series
@end
@implementation BMWCar5Series
@end
@implementation BMWCar7Series
@end
Lisence.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Lisence : NSObject
- (NSInteger)lisenceNumber;
@end
@interface CarLisence : Lisence
@end
NS_ASSUME_NONNULL_END
Lisence.m
#import "Lisence.h"
@implementation Lisence
- (BOOL)isKindOfClass:(Class)aClass
{
if (!aClass) {
return NO;
}
Class cls = self.class;
while (cls != aClass) {
cls = [cls superclass];
if (cls == NSObject.class) {
return NO;
}
}
return YES;
}
- (NSInteger)lisenceNumber
{
return [[self class] hash];
}
@end
@implementation CarLisence
@end
Factory.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Factory : NSObject
- (id)make:(NSString *)clsName;
@end
NS_ASSUME_NONNULL_END
Factory.m
#import "Factory.h"
@implementation Factory
- (id)make:(NSString *)clsName
{
if (clsName) {
Class cls = NSClassFromString(clsName);
return [[cls alloc] init];
}
return nil;
}
@end
調(diào)用:
Factory *fac = [[Factory alloc] init];
BMWCar7Series *car = [fac make:NSStringFromClass(BMWCar7Series.class)];
CarLisence *lis = [fac make:NSStringFromClass(CarLisence.class)];
NSLog(@"%@,%ld",car.carName,lis.lisenceNumber);
結(jié)果:
MainProject[55135:11368132] BMWCar7Series,4408563744
在iOS具體開發(fā)中的作用
- 提供快捷創(chuàng)建實例對象的方法,而不必關(guān)心具體的創(chuàng)建細(xì)節(jié)
- 提供復(fù)雜對象的創(chuàng)建和使用分離的方法,便于解耦(路由常用的方法)
五、建造者模式
模式的定義與特點
建造者(Builder)模式的定義:指將一個復(fù)雜對象的構(gòu)造與它的表示分離,使同樣的構(gòu)建過程可以創(chuàng)建不同的表示,這樣的設(shè)計模式被稱為建造者模式。它是將一個復(fù)雜的對象分解為多個簡單的對象,然后一步一步構(gòu)建而成。它將變與不變相分離,即產(chǎn)品的組成部分是不變的,但每一部分是可以靈活選擇的。
該模式的主要優(yōu)點如下:
- 各個具體的建造者相互獨立,有利于系統(tǒng)的擴(kuò)展。
- 客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié),便于控制細(xì)節(jié)風(fēng)險。
其缺點如下:
- 產(chǎn)品的組成部分必須相同,這限制了其使用范圍。
- 如果產(chǎn)品的內(nèi)部變化復(fù)雜,該模式會增加很多的建造者類。
建造者(Builder)模式和工廠模式的關(guān)注點不同:建造者模式注重零部件的組裝過程,而工廠方法模式更注重零部件的創(chuàng)建過程,但兩者可以結(jié)合使用。
模式的結(jié)構(gòu)與實現(xiàn)
建造者(Builder)模式由產(chǎn)品、抽象建造者、具體建造者、指揮者等 4 個要素構(gòu)成,現(xiàn)在我們來分析其基本結(jié)構(gòu)和實現(xiàn)方法。
模式的結(jié)構(gòu)
建造者(Builder)模式的主要角色如下。
-
產(chǎn)品角色(Product):它是包含多個組成部件的復(fù)雜對象,由具體建造者來創(chuàng)建其各個滅部件。 -
抽象建造者(Builder):它是一個包含創(chuàng)建產(chǎn)品各個子部件的抽象方法的接口,通常還包含一個返回復(fù)雜產(chǎn)品的方法 getResult()。 -
具體建造者(Concrete Builder):實現(xiàn) Builder 接口,完成復(fù)雜產(chǎn)品的各個部件的具體創(chuàng)建方法。 -
指揮者(Director):它調(diào)用建造者對象中的部件構(gòu)造與裝配方法完成復(fù)雜對象的創(chuàng)建,在指揮者中不涉及具體產(chǎn)品的信息。
建造者模式在Objective-C中的設(shè)計
事實上,在OC里,UITableViewController就實現(xiàn)了建造者(Builder)模式。我們看下UITableViewController的頭文件:
@property (nonatomic, strong, null_resettable) UITableView *tableView;
@property (nonatomic) BOOL clearsSelectionOnViewWillAppear NS_AVAILABLE_IOS(3_2); // defaults to YES. If YES, any selection is cleared in viewWillAppear:
@property (nonatomic, strong, nullable) UIRefreshControl *refreshControl NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;
當(dāng)在其他頁面調(diào)用創(chuàng)建方法- (instancetype)initWithStyle:(UITableViewStyle)style的時候,就會自動建造UITableView和UIRefreshControl這兩個部件實例,建造完畢后,統(tǒng)一返回UITableViewController這個實例對象