關(guān)于設(shè)計模式及其在iOS中的實踐(三)

創(chuàng)建型設(shè)計模式在iOS中的實踐

一、單例模式

單例模式的定義與特點

單例(Singleton)模式的定義:指一個類全局只有一個實例,且該類能自行創(chuàng)建這個實例的一種模式。

單例模式有 3 個特點:

  1. 單例類只有一個實例對象;
  2. 該單例對象必須由單例類自行創(chuàng)建;
  3. 單例類對外提供一個訪問該單例的全局訪問點;

單例模式的結(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)

原型模式包含以下主要角色。

  1. 抽象原型類:規(guī)定了具體原型對象必須實現(xiàn)的接口。
  2. 具體原型類:實現(xiàn)抽象原型類的 clone() 方法,它是可被復(fù)制的對象。
  3. 訪問類:使用具體原型類中的 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)

工廠方法模式的主要角色如下。

  1. 工廠(Factory):提供了創(chuàng)建產(chǎn)品的接口,調(diào)用者通過它訪問具體工廠的工廠方法 newProduct() 來創(chuàng)建產(chǎn)品。
  2. 產(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、NSIntegerCGFloat

    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)點如下:

  1. 各個具體的建造者相互獨立,有利于系統(tǒng)的擴(kuò)展。
  2. 客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié),便于控制細(xì)節(jié)風(fēng)險。

其缺點如下:

  1. 產(chǎn)品的組成部分必須相同,這限制了其使用范圍。
  2. 如果產(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)模式的主要角色如下。

  1. 產(chǎn)品角色(Product):它是包含多個組成部件的復(fù)雜對象,由具體建造者來創(chuàng)建其各個滅部件。
  2. 抽象建造者(Builder):它是一個包含創(chuàng)建產(chǎn)品各個子部件的抽象方法的接口,通常還包含一個返回復(fù)雜產(chǎn)品的方法 getResult()。
  3. 具體建造者(Concrete Builder):實現(xiàn) Builder 接口,完成復(fù)雜產(chǎn)品的各個部件的具體創(chuàng)建方法。
  4. 指揮者(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的時候,就會自動建造UITableViewUIRefreshControl這兩個部件實例,建造完畢后,統(tǒng)一返回UITableViewController這個實例對象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容