創(chuàng)建型設(shè)計(jì)模式在iOS中的實(shí)踐
一、單例模式
單例模式的定義與特點(diǎn)
單例(Singleton)
模式的定義:指一個(gè)類(lèi)全局只有一個(gè)實(shí)例逼纸,且該類(lèi)能自行創(chuàng)建這個(gè)實(shí)例的一種模式。
單例模式有 3 個(gè)特點(diǎn):
- 單例類(lèi)只有一個(gè)實(shí)例對(duì)象;
- 該單例對(duì)象必須由單例類(lèi)自行創(chuàng)建盆均;
- 單例類(lèi)對(duì)外提供一個(gè)訪(fǎng)問(wèn)該單例的全局訪(fǎng)問(wèn)點(diǎn);
單例模式的結(jié)構(gòu)與實(shí)現(xiàn)
單例模式是設(shè)計(jì)模式中最簡(jiǎn)單的模式之一漱逸。通常泪姨,普通類(lèi)的構(gòu)造函數(shù)是公有的,外部類(lèi)可以通過(guò)“new 構(gòu)造函數(shù)()”來(lái)生成多個(gè)實(shí)例饰抒。但是肮砾,如果將類(lèi)的構(gòu)造函數(shù)設(shè)為私有的,外部類(lèi)就無(wú)法調(diào)用該構(gòu)造函數(shù)袋坑,也就無(wú)法生成多個(gè)實(shí)例仗处。這時(shí)該類(lèi)自身必須定義一個(gè)靜態(tài)私有實(shí)例,并向外提供一個(gè)靜態(tài)的公有函數(shù)用于創(chuàng)建或獲取該靜態(tài)私有實(shí)例枣宫。
下面來(lái)分析其基本結(jié)構(gòu)和實(shí)現(xiàn)方法婆誓。
單例模式的結(jié)構(gòu)
單例模式的主要角色如下。
- 單例類(lèi):包含一個(gè)實(shí)例且能自行創(chuàng)建這個(gè)實(shí)例的類(lèi)。
- 訪(fǎng)問(wèn)類(lèi):使用單例的類(lèi)。
單例模式在Objective-C中的設(shè)計(jì)
[UIApplication sharedApplication]
-
[NSUserDefaults standardUserDefaults];
這是OC里良好設(shè)計(jì)的一個(gè)具體例子
創(chuàng)建屬于自己的單例
創(chuàng)建的單例應(yīng)該滿(mǎn)足以下要求:
- 全局唯一
- 線(xiàn)程安全
- 性能開(kāi)銷(xiāo)小
我們將采用GCD
來(lái)創(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具體開(kāi)發(fā)中的作用
- 提供全局的緩存數(shù)據(jù)的保存和訪(fǎng)問(wèn)入口(如token拟杉、username等)
- 提供全局狀態(tài)的監(jiān)聽(tīng)
- 避免創(chuàng)建大量重復(fù)的對(duì)象(如網(wǎng)絡(luò)訪(fǎng)問(wèn)的管理類(lèi))
二、原型模式
原型模式的定義與特點(diǎn)
原型(Prototype)
模式的定義如下:用一個(gè)已經(jīng)創(chuàng)建的實(shí)例作為原型文留,通過(guò)復(fù)制該原型對(duì)象來(lái)創(chuàng)建一個(gè)和原型相同或相似的新對(duì)象好唯。在這里,原型實(shí)例指定了要?jiǎng)?chuàng)建的對(duì)象的種類(lèi)燥翅。用這種方式創(chuàng)建對(duì)象非常高效骑篙,根本無(wú)須知道對(duì)象創(chuàng)建的細(xì)節(jié)。
原型模式的結(jié)構(gòu)與實(shí)現(xiàn)
模式的結(jié)構(gòu)
原型模式包含以下主要角色森书。
- 抽象原型類(lèi):規(guī)定了具體原型對(duì)象必須實(shí)現(xiàn)的接口靶端。
- 具體原型類(lèi):實(shí)現(xiàn)抽象原型類(lèi)的 clone() 方法,它是可被復(fù)制的對(duì)象拄氯。
- 訪(fǎng)問(wèn)類(lèi):使用具體原型類(lèi)中的 clone() 方法來(lái)復(fù)制新的對(duì)象躲查。
原型模式在Objective-C中的設(shè)計(jì)
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具體開(kāi)發(fā)中的作用
- 提供復(fù)雜對(duì)象的快速創(chuàng)建(如具有包含多層嵌套對(duì)象的屬性的對(duì)象的創(chuàng)建)
- 提供向局域外傳遞和使用對(duì)象的方式(如
[block copy]
)
三、工廠(chǎng)模式
模式的定義與特點(diǎn)
工廠(chǎng)方法(FactoryMethod)
模式的定義:定義一個(gè)創(chuàng)建產(chǎn)品對(duì)象的工廠(chǎng)接口译柏,將產(chǎn)品對(duì)象的實(shí)際創(chuàng)建工作推遲到具體子工廠(chǎng)類(lèi)當(dāng)中镣煮。這滿(mǎn)足創(chuàng)建型模式中所要求的“創(chuàng)建與使用相分離”的特點(diǎn)。
我們把被創(chuàng)建的對(duì)象稱(chēng)為“產(chǎn)品”鄙麦,把創(chuàng)建產(chǎn)品的對(duì)象稱(chēng)為“工廠(chǎng)”典唇。工廠(chǎng)方法模式的主要優(yōu)點(diǎn)有:
- 用戶(hù)只需要知道具體工廠(chǎng)的名稱(chēng)就可得到所要的產(chǎn)品,無(wú)須知道產(chǎn)品的具體創(chuàng)建過(guò)程胯府;
- 在系統(tǒng)增加新的產(chǎn)品時(shí)只需要添加具體產(chǎn)品類(lèi)和對(duì)應(yīng)的具體工廠(chǎng)類(lèi)介衔,無(wú)須對(duì)原工廠(chǎng)進(jìn)行任何修改,滿(mǎn)足開(kāi)閉原則骂因;
其缺點(diǎn)是:每增加一個(gè)產(chǎn)品就要增加一個(gè)具體產(chǎn)品類(lèi)和一個(gè)對(duì)應(yīng)的具體工廠(chǎng)類(lèi)炎咖,這增加了系統(tǒng)的復(fù)雜度。
模式的結(jié)構(gòu)與實(shí)現(xiàn)
在典型的工廠(chǎng)方法模式由工廠(chǎng)和產(chǎn)品2個(gè)要素構(gòu)成寒波。根據(jù)單一職責(zé)原則乘盼,工廠(chǎng)負(fù)責(zé)創(chuàng)建產(chǎn)品,產(chǎn)品則負(fù)責(zé)被使用俄烁。本節(jié)來(lái)分析其基本結(jié)構(gòu)和實(shí)現(xiàn)方法绸栅。
模式的結(jié)構(gòu)
工廠(chǎng)方法模式的主要角色如下。
- 工廠(chǎng)(Factory):提供了創(chuàng)建產(chǎn)品的接口页屠,調(diào)用者通過(guò)它訪(fǎng)問(wèn)具體工廠(chǎng)的工廠(chǎng)方法 newProduct() 來(lái)創(chuàng)建產(chǎn)品粹胯。
- 產(chǎn)品(Product):實(shí)現(xiàn)了抽象產(chǎn)品角色所定義的接口,由具體工廠(chǎng)來(lái)創(chuàng)建辰企,它同具體工廠(chǎng)之間一一對(duì)應(yīng)风纠。
工廠(chǎng)模式在Objective-C中的設(shè)計(jì)
在講工廠(chǎng)模式的代表設(shè)計(jì)之前,需要了解下OC中的類(lèi)簇這個(gè)概念
在這里不具體展開(kāi)牢贸,可以參考以下這篇文章:類(lèi)簇(class cluster)
言歸正傳议忽,我們將通過(guò)NSString
來(lái)看看什么是工廠(chǎng)模式
const char *cr = "bbc";
NSString *astr = [NSString stringWithCString:cr encoding:NSUTF8StringEncoding];
NSLog(@"%@,%@",[@"abc" class],[astr class]);
__NSCFConstantString,NSTaggedPointerString
可以看出來(lái),NSString
是一個(gè)類(lèi)簇十减,這是一個(gè)抽象類(lèi)栈幸,它提供了各種子類(lèi)的創(chuàng)建方法,但外部統(tǒng)一通過(guò)NSString
來(lái)調(diào)用帮辟,并且所有的子類(lèi)都可以使用NSString
對(duì)外提供的一系列實(shí)例方法速址。
NSLog(@"%@,%@",[str substringFromIndex:1],[astr substringFromIndex:1]);
bc,bc
比較有意思的是,NSString
既是工廠(chǎng)string factory
由驹,又是產(chǎn)品string
的抽象類(lèi)
關(guān)于工廠(chǎng)的自定義創(chuàng)建芍锚,我將在下一節(jié)結(jié)合抽象工廠(chǎng)模式再進(jìn)行具體的演示
四、抽象工廠(chǎng)模式
模式的定義與特點(diǎn)
抽象工廠(chǎng)(AbstractFactory)
模式的定義:是一種為訪(fǎng)問(wèn)類(lèi)提供一個(gè)創(chuàng)建一組相關(guān)或相互依賴(lài)對(duì)象的接口蔓榄,且訪(fǎng)問(wèn)類(lèi)無(wú)須指定所要產(chǎn)品的具體類(lèi)就能得到同族的不同等級(jí)的產(chǎn)品的模式結(jié)構(gòu)并炮。
使用抽象工廠(chǎng)模式一般要滿(mǎn)足以下條件。
- 系統(tǒng)中有多個(gè)產(chǎn)品族甥郑,每個(gè)具體工廠(chǎng)創(chuàng)建同一族但屬于不同等級(jí)結(jié)構(gòu)的產(chǎn)品逃魄。
- 系統(tǒng)一次只可能消費(fèi)其中某一族產(chǎn)品,即同族的產(chǎn)品一起使用澜搅。
抽象工廠(chǎng)模式除了具有工廠(chǎng)方法模式的優(yōu)點(diǎn)外伍俘,其他主要優(yōu)點(diǎn)如下。
- 可以在類(lèi)的內(nèi)部對(duì)產(chǎn)品族中相關(guān)聯(lián)的多等級(jí)產(chǎn)品共同管理勉躺,而不必專(zhuān)門(mén)引入多個(gè)新的類(lèi)來(lái)進(jìn)行管理癌瘾。
- 當(dāng)增加一個(gè)新的產(chǎn)品族時(shí)不需要修改原代碼,滿(mǎn)足開(kāi)閉原則饵溅。
抽象工廠(chǎng)模式的結(jié)構(gòu)與實(shí)現(xiàn)
模式的結(jié)構(gòu)
抽象工廠(chǎng)模式的主要角色如下妨退。
-
抽象工廠(chǎng)(Abstract Factory)
:提供了創(chuàng)建產(chǎn)品的接口,它包含多個(gè)創(chuàng)建產(chǎn)品的方法 newProduct()蜕企,可以創(chuàng)建多個(gè)不同等級(jí)的產(chǎn)品咬荷。 -
具體工廠(chǎng)(Concrete Factory)
:主要是實(shí)現(xiàn)抽象工廠(chǎng)中的多個(gè)抽象方法,完成具體產(chǎn)品的創(chuàng)建糖赔。 -
抽象產(chǎn)品(Product)
:定義了產(chǎn)品的規(guī)范萍丐,描述了產(chǎn)品的主要特性和功能,抽象工廠(chǎng)模式有多個(gè)抽象產(chǎn)品放典。 -
具體產(chǎn)品(ConcreteProduct)
:實(shí)現(xiàn)了抽象產(chǎn)品角色所定義的接口逝变,由具體工廠(chǎng)來(lái)創(chuàng)建,它 同具體工廠(chǎng)之間是多對(duì)一的關(guān)系奋构。
抽象工廠(chǎng)模式與工廠(chǎng)模式的異同
相同點(diǎn):
- 都屬于創(chuàng)建型模式
- 都能夠?qū)崿F(xiàn)創(chuàng)建和使用分離
不同點(diǎn):
- 工廠(chǎng)模式用于創(chuàng)建同一種類(lèi)的不同等級(jí)(或款式)的產(chǎn)品壳影,而抽象工廠(chǎng)模式用于創(chuàng)建不同種類(lèi)的產(chǎn)品。例如弥臼,如果輪胎是一個(gè)抽象的產(chǎn)品類(lèi)宴咧,那么自行車(chē)輪胎、摩托車(chē)輪胎和汽車(chē)輪胎就是不同款式的產(chǎn)品径缅;但如果認(rèn)為汽車(chē)輪胎是抽象類(lèi)掺栅,那么米其林輪胎和普利司通輪胎就是不同款式的產(chǎn)品烙肺。
- 工廠(chǎng)模式可以直接生產(chǎn)產(chǎn)品,抽象工廠(chǎng)模式需要通過(guò)抽象工廠(chǎng)先創(chuàng)建工廠(chǎng)氧卧,再生產(chǎn)產(chǎn)品桃笙,可以簡(jiǎn)單理解為,抽象工廠(chǎng)是工廠(chǎng)模式的二次工廠(chǎng)化
抽象工廠(chǎng)模式在Objective-C中的設(shè)計(jì)
用工廠(chǎng)模式創(chuàng)建了一個(gè)NSNumber
NSNumber *num = [NSNumber numberWithBool:YES];
用抽象工廠(chǎng)模式依次創(chuàng)建了NSString
沙绝、NSInteger
搏明、CGFloat
NSString *str = num.stringValue;
NSInteger i = num.integerValue;
CGFloat f = num.floatValue;
創(chuàng)建屬于自己的工廠(chǎng)、抽象工廠(chǎng)模式
值得說(shuō)明的是闪檬,因?yàn)镺C的動(dòng)態(tài)性星著,可以只用創(chuàng)建一個(gè)抽象工廠(chǎng)類(lèi),即可實(shí)現(xiàn)一套方法創(chuàng)建不同種類(lèi)不同款式的產(chǎn)品
1粗悯、設(shè)定兩類(lèi)抽象產(chǎn)品(產(chǎn)品族):寶馬汽車(chē)類(lèi)和許可類(lèi)
2虚循、設(shè)定具體的產(chǎn)品(產(chǎn)品系列):3系,5系为黎,7系邮丰,汽車(chē)許可
3、設(shè)定抽象工廠(chǎng)
文件目錄如下:
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具體開(kāi)發(fā)中的作用
- 提供快捷創(chuàng)建實(shí)例對(duì)象的方法铭乾,而不必關(guān)心具體的創(chuàng)建細(xì)節(jié)
- 提供復(fù)雜對(duì)象的創(chuàng)建和使用分離的方法剪廉,便于解耦(路由常用的方法)
五、建造者模式
模式的定義與特點(diǎn)
建造者(Builder)模式
的定義:指將一個(gè)復(fù)雜對(duì)象的構(gòu)造與它的表示分離炕檩,使同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示斗蒋,這樣的設(shè)計(jì)模式被稱(chēng)為建造者模式。它是將一個(gè)復(fù)雜的對(duì)象分解為多個(gè)簡(jiǎn)單的對(duì)象笛质,然后一步一步構(gòu)建而成泉沾。它將變與不變相分離,即產(chǎn)品的組成部分是不變的妇押,但每一部分是可以靈活選擇的跷究。
該模式的主要優(yōu)點(diǎn)如下:
- 各個(gè)具體的建造者相互獨(dú)立,有利于系統(tǒng)的擴(kuò)展敲霍。
- 客戶(hù)端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)俊马,便于控制細(xì)節(jié)風(fēng)險(xiǎn)。
其缺點(diǎn)如下:
- 產(chǎn)品的組成部分必須相同肩杈,這限制了其使用范圍柴我。
- 如果產(chǎn)品的內(nèi)部變化復(fù)雜,該模式會(huì)增加很多的建造者類(lèi)扩然。
建造者(Builder)模式和工廠(chǎng)模式的關(guān)注點(diǎn)不同:建造者模式注重零部件的組裝過(guò)程艘儒,而工廠(chǎng)方法模式更注重零部件的創(chuàng)建過(guò)程,但兩者可以結(jié)合使用。
模式的結(jié)構(gòu)與實(shí)現(xiàn)
建造者(Builder)模式由產(chǎn)品界睁、抽象建造者觉增、具體建造者、指揮者等 4 個(gè)要素構(gòu)成翻斟,現(xiàn)在我們來(lái)分析其基本結(jié)構(gòu)和實(shí)現(xiàn)方法抑片。
模式的結(jié)構(gòu)
建造者(Builder)模式的主要角色如下。
-
產(chǎn)品角色(Product)
:它是包含多個(gè)組成部件的復(fù)雜對(duì)象杨赤,由具體建造者來(lái)創(chuàng)建其各個(gè)滅部件。 -
抽象建造者(Builder)
:它是一個(gè)包含創(chuàng)建產(chǎn)品各個(gè)子部件的抽象方法的接口截汪,通常還包含一個(gè)返回復(fù)雜產(chǎn)品的方法 getResult()疾牲。 -
具體建造者(Concrete Builder)
:實(shí)現(xiàn) Builder 接口,完成復(fù)雜產(chǎn)品的各個(gè)部件的具體創(chuàng)建方法衙解。 -
指揮者(Director)
:它調(diào)用建造者對(duì)象中的部件構(gòu)造與裝配方法完成復(fù)雜對(duì)象的創(chuàng)建阳柔,在指揮者中不涉及具體產(chǎn)品的信息。
建造者模式在Objective-C中的設(shè)計(jì)
事實(shí)上蚓峦,在OC里舌剂,UITableViewController
就實(shí)現(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)在其他頁(yè)面調(diào)用創(chuàng)建方法- (instancetype)initWithStyle:(UITableViewStyle)style
的時(shí)候暑椰,就會(huì)自動(dòng)建造UITableView
和UIRefreshControl
這兩個(gè)部件實(shí)例霍转,建造完畢后,統(tǒng)一返回UITableViewController
這個(gè)實(shí)例對(duì)象