免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
《創(chuàng)建型設計模式》之iOS系統(tǒng)框架實踐 | 輕墨

《創(chuàng)建型設計模式》之iOS系統(tǒng)框架實踐

為了API的易用性、易維護性和健壯性,蘋果工程師在iOS系統(tǒng)框架中其實運用了不少經(jīng)典設計模式,而這些實踐也正是因為良好的封裝性,開發(fā)中我們雖日日相對,卻也難以察覺它的存在。相對于其他生搬硬造隔靴搔癢的例子,這些我們熟悉的不能再熟悉的API方是學習設計模式的最佳案例。因此本系列擬以iOS系統(tǒng)框架中相關(guān)設計模式的實踐為起點,探討研究23種經(jīng)典設計模式。

本文先講述《創(chuàng)建型設計模式》(Creational Patterns)。

創(chuàng)建型設計模式是在創(chuàng)建一個類時用到的設計模式,總共有5種,其中工廠方法模式還可以根據(jù)實現(xiàn)的不同,分出簡單工廠模式和工廠方法模式。

簡單工廠模式(Factory Method)

iOS系統(tǒng)Foundation框架中的NSNumber所應用的就是簡單工廠模式。

簡單工廠模式主要解決不同情況下,需要創(chuàng)建不同子類,而這些子類又需要轉(zhuǎn)化為公共父類讓外界去使用的問題,因為這樣對外接口只有一個,實際行為卻因子類的具體實現(xiàn)而不同。拿NSNumber來說,傳入Int、Float、Double、CharUnsignedChar等具體number,NSNumber返回的是對應的NSNumber子類,而我們使用時只知NSNumber,不知具體的子類。

1
2
3
4
5
6
7
8
9
import UIKit
let boolValue: Bool = true
let doubleValue: Double = 1.0

let boolN = NSNumber(value: boolValue)
let doubleN = NSNumber(value: doubleValue)

print(type(of:boolN))
print(type(of:doubleN))

輸出結(jié)果為

1
2
__NSCFBoolean
__NSCFNumber

如果用簡單工廠方法實現(xiàn)NSNumber(為了不與系統(tǒng)的NSNumber混淆,本文自己定義的NSNumber均去掉NS前綴,改為Number),代碼大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 抽象產(chǎn)品
protocol Number {
func doubleValue() -> Double
func boolValue() -> Bool
}

// 生產(chǎn)工廠
class NumberFactory {
func createNumber(value: Bool) -> Number {
return __NSCFBoolean(value: value)
}
func createNumber(value: Double) -> Number {
return __CFNumber(value: value)
}
}

// 具體的產(chǎn)品A
private class __CFBoolean: Number {
let bool: Bool
init(value: Bool) {
bool = value
}

func doubleValue() -> Double {
return bool ? 1 : 0
}

func boolValue() -> Bool {
return bool
}
}

// 具體的產(chǎn)品B
private class __CFNumber: Number {
let double: Double
init(value: Double) {
double = value
}

func doubleValue() -> Double {
return double
}

func boolValue() -> Bool {
return double != 0
}
}

其中Number是抽象協(xié)議,負責定義行為,而__CFNumber__CFBoolean是實現(xiàn)了Number抽象協(xié)議的私有實體類,NumberFactory則是一個創(chuàng)建Number的工廠。

具體使用時,先創(chuàng)建工廠,然后根據(jù)需要創(chuàng)建具體的實體類:

1
2
3
4
5
// 先創(chuàng)建工廠
let factory = NumberFactory()
// 然后根據(jù)需要創(chuàng)建實體類
let boolN = factory.createNumber(value: false)
let doubleN = factory.createNumber(value: 2.0)

而由于Objective-C的初始化方法中可以直接返回子類型,因此不必創(chuàng)建一個單獨的工廠類NumberFactory,直接將相應的工廠方法邏輯封裝在NSNumberinit方法中即可:

1
2
3
4
5
6
7
8
@implementation NSNumber
- (NSNumber *)initWithBool:(BOOL)value {
return [[__NSCFBoolean alloc] initWithBool:value];
}
- (NSNumber *)initWithDouble:(double)value {
return [[__NSCFNumber alloc] initWithDouble:value];
}
@end

而在Swift中不可能從init初始化方法中返回一個子類。(Swiftinit方法除了return nil外不能有返回值)

工廠方法模式(Factory Method)

簡單工廠模式中,工廠只有一個實體類NumberFactory,每當添加新的產(chǎn)品(即新實現(xiàn)Number協(xié)議的子類),都需要去修改這個工廠。
比如上文新添加一個針對Float實現(xiàn)Number協(xié)議的__CFFloat(系統(tǒng)中的NSNumber并沒有實體子類__NSCFFloat,而是所有的數(shù)字類型都封裝為__NSCFNumber),

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 新添加的具體的產(chǎn)品C
private class __CFFloat: Number {
let float: Float
init(value: Float) {
float = value
}

func doubleValue() -> Double {
return Double(float)
}

func boolValue() -> Bool {
return float != 0
}
}

那么NumberFactory也需要改動:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 生產(chǎn)工廠
class NumberFactory {
func createNumber(value: Bool) -> Number {
return __NSCFBoolean(value: value)
}
func createNumber(value: Double) -> Number {
return __CFNumber(value: value)
}
// 新添加的工廠方法
func createNumber(value: Float) -> Number {
return __CFFloat(value: value)
}
}

為解決這個弊端,可以將工廠NumberFactory也抽象一層,定義為一個協(xié)議:

1
2
3
4
// 抽象工廠
protocol NumberFactory {
func createNumber(value: Any) -> Number
}

然后針對不同的Number實體子類,都定義相應的工廠NumberFactory子類即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Bool 專用的工廠類
class BoolNumberFactory: NumberFactory {
func createNumber(value: Any) -> Number {
guard let value = value as? Bool else {
fatalError("value must be a bool")
}
return __CFBoolean(value)
}
}
// Double 專用的工廠類
class DoubleNumberFactory: NumberFactory {
func createNumber(value: Any) -> Number {
guard let value = value as? Double else {
fatalError("value must be a double")
}
return __CFNumber(value)
}
}

具體使用中,先創(chuàng)建工廠,然后直接根據(jù)相應的工廠創(chuàng)建相應的Number

1
2
3
4
5
6
7
// 先創(chuàng)建工廠
let boolFactory = BoolNumberFactory()
let doubleFactory = DoubleNumberFactory()

// 然后直接根據(jù)相應的工廠創(chuàng)建相應的Number
let boolN = boolFactory.createNumber(value: false)
let doubleN = doubleFactory.createNumber(value: 2.0)

如果想新添加一個針對Float實現(xiàn)Number協(xié)議的__CFFloat,添加完成后,直接再添加一個對應的NumberFactory子類即可。

1
2
3
4
5
6
7
8
9
// Float 專用的工廠類
class FloatNumberFactory: NumberFactory {
func createNumber(value: Any) -> Number {
guard let value = value as? Float else {
fatalError("value must be a float")
}
return __CFFloat(value)
}
}

這就是工廠方法模式與簡單工廠模式的區(qū)別,即工廠方法模式不但抽象了產(chǎn)品,而且抽象了工廠。

抽象工廠模式(Abstract Factory)

工廠方法模式抽象了工廠,但只負責生產(chǎn)一種產(chǎn)品。抽象工廠模式與工廠方法模式一般無二,都是抽象了工廠和產(chǎn)品,只是抽象工廠模式中的抽象工廠會負責生產(chǎn)一種以上相關(guān)聯(lián)、會一起使用的產(chǎn)品。
還是以Number的抽象工廠NumberFactory舉例。Foundation中類似NSNumber類簇的,還有NSArray:

1
2
3
4
5
6
7
8
import UIKit
let array0 = NSArray(array: [])
let array1 = NSArray(arrayLiteral: 1, 2)
let array2 = NSArray(arrayLiteral: 1)

print(type(of:array0))
print(type(of:array1))
print(type(of:array2))

打印結(jié)果如下:

1
2
3
__NSArray0
__NSArrayI
__NSSingleObjectArrayI

定義NSArray的抽象協(xié)議并實現(xiàn)兩個私有類__CArray0__CArrayI,為不與系統(tǒng)中的NSArrayArray混淆,這里取CArray

1
2
3
4
5
6
7
8
9
10
11
12
13
protocol CArray {
var count: Int { get }
// 其他公共接口
}
private class __CArray0: CArray {
var count = 0
// 其他公共接口實現(xiàn)

}
private class __CArrayI: CArray {
var count = 0
// 其他公共接口實現(xiàn)
}

定義NumberCArray的抽象工廠協(xié)議NumberAndArrayFactory

1
2
3
4
5
6
protocol NumberAndArrayFactory {
// 用來生產(chǎn)Number的工廠方法
func createNumber(value: Any) -> Number
// 用來生產(chǎn)CArray的工廠方法
func createCArray() -> CArray
}

定義抽象工廠NumberAndArrayFactory的具體實現(xiàn)類BoolNumberAndArray0Factory:

1
2
3
4
5
6
7
8
9
10
11
12
class BoolNumberAndArray0Factory: NumberAndArrayFactory {
func createNumber(value: Any) -> Number {
guard let value = value as? Bool else {
fatalError("value must be a bool")
}
return __CFBoolean(value)
}

func createCArray() -> CArray {
return __CArray0()
}
}

具體使用時,先創(chuàng)建抽象工廠NumberAndArrayFactory的具體實現(xiàn)類,然后在調(diào)用這個實現(xiàn)類上的工廠方法,創(chuàng)建相應的產(chǎn)品Number或者CArray

1
2
3
4
5
6
// 創(chuàng)建抽象工廠`NumberAndArrayFactory`的具體實現(xiàn)類
let boolNumberAndArray0Factory = BoolNumberAndArray0Factory()

// 調(diào)用工廠方法,創(chuàng)建相應的產(chǎn)品
let boolNumber = boolNumberAndArray0Factory.createNumber(true)
let 0CArray = boolNumberAndArray0Factory.createCArray()

需要注意的是,這里為了說明抽象工廠模式,抽象工廠NumberAndArrayFactory所創(chuàng)建的NumberCArray沒有任何關(guān)聯(lián),在實際項目中,同一抽象工廠所創(chuàng)建的產(chǎn)品是關(guān)聯(lián)的,一般是一起結(jié)合使用,如果不關(guān)聯(lián),也不必用抽象工廠模式。

建造者模式(builder)

建造者模式是用來隔離復雜對象的配置過程,將復雜對象的配置過程單獨封裝成一個builder對象,完成配置后,再獲取配置完成的實例對象。

cocoa中使用建造者模式的類是NSDateComponents,

1
2
3
4
5
6
7
8
9
10
11
12
import Foundation

var builder = NSDateComponents()

builder.hour = 10
builder.day = 6
builder.month = 9
builder.year = 1940
builder.calendar = Calendar(identifier: .gregorian)

var date = builder.date
print(date!)

輸出結(jié)果為:

1
1940-09-06 01:00:00 +0000

NSDateComponents相當于日期的一個builder,NSDateComponents用來配置日期的各個部分,配置完成后,最終獲取對應的Date日期。
NSDateComponents的實現(xiàn)大致如下(為避免與系統(tǒng)中的NSDateComponentsDateComponents混淆,這里取DateBuilder):

1
2
3
4
5
6
7
8
9
10
11
12
13
class DateBuilder {
var hour = 0
var day = 0
var month = 0
var year = 1970
var calendar = Calendar(identifier: .gregorian)

var date: Date {
// 根據(jù)date components計算日期,比較復雜,這里省略了計算過程
let calculatedDate = ...
return calculatedDate
}
}

但這使用上不能鏈式調(diào)用,很不方便,加上各個屬性的設置方法,返回自己本身,可以實現(xiàn)鏈式調(diào)用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class DateBuilder {
var hour = 0
var day = 0
var month = 0
var year = 1970
var calendar = Calendar(identifier: .gregorian)

var date: Date {
// 根據(jù)DateComponents計算日期,比較復雜,這里省略了計算過程
let calculatedDate = ...
return calculatedDate
}
func hour(_ hour: Int) -> DateBuilder {
self.hour = hour
return self
}
func day(_ day: Int) -> DateBuilder {
self.day = day
return self
}
func month(_ month: Int) -> DateBuilder {
self.month = month
return self
}
func year(_ year: Int) -> DateBuilder {
self.year = year
return self
}
func calendar(_ calendar: Calendar) -> DateBuilder {
self.calendar = calendar
return self
}
}

使用時很方便:

1
let date = DateBuilder().hour(1).day(2).month(12).year(2017).date

原型模式(Prototype)

原型模式其實就是一個類能夠通過自身copy,創(chuàng)建一個內(nèi)容一模一樣的新實例,這在iOS的系統(tǒng)框架Foundation中挺常見的,NSString、NSArrayNSDictionaryNSParagraphStylecopy、mutableCopy方法都能復制一個新的實例,從而免去了從零創(chuàng)建一個復雜類的麻煩。
NSParagraphStyle,當獲取到一個paragraphStyle之后,突然又想在其基礎(chǔ)上改動同時又不想直接改變原來NSParagraphStyle,最方便的不過copy一份原來的,然后在改動。

1
2
3
4
let paragraphStyle = NSParagraphStyle.default
let mutablePara = paragraphStyle.mutableCopy() as! NSMutableParagraphStyle
mutablePara.lineSpacing = 10
mutablePara.paragraphSpacing = 5

如果想實現(xiàn)原型模式,在swift中直接實現(xiàn)NSCopyingNSMutableCopying協(xié)議即可。

單例模式(Singleton)

單例模式即一個類至始至終只有一個實例(單例類是可以新創(chuàng)建實例的,但一般都會用公共的那個單例實例),常用于Manager上。單例在iOS系統(tǒng)中十分常用,如NSParagraphStyle.defaultUIScreen.main 、 UIApplication.shared 、 UserDefaults.standardFileManager.default等都是單例。

1
2
3
4
5
let paragraphStyle = NSParagraphStyle.default
let screen = UIScreen.main
let application = UIApplication.shared
let userDefault = UserDefaults.standard
let fileManager = FileManager.default

在swift中實現(xiàn)一個單例模式,也是非常簡單的。

1
2
3
4
5
6
7
8
class Manager {
// 單例
static let shared = Manager()
// 私有化后,這個對象只會有單例這一個實例
private init() {

}
}

上述單例,初始化方法私有化了,因此在整個APP的生命周期中,將只有一個此類的實例,即單例。
但有時,單利只是給一個默認配置而已,如果想自定義,可以完全重新初始化一個新的實例,如

1
2
3
4
5
6
7
8
class ParagraphStyle {
// 單例
static let default = ParagraphStyle()
// 沒有私有化,這個對象如果有需要可以創(chuàng)建單例以外的新的實例
init() {

}
}

總結(jié)

創(chuàng)建型設計模式在iOS系統(tǒng)中的運用相當廣泛,而我們開發(fā)中只要有一定的抽象,基本都會用到,尤其是簡單工廠模式和工廠模式、單例模式,希望本文的講解能讓大家能真正理解這些開發(fā)模式,并在開發(fā)中順利應用。

參考文章:

  1. Class Clusters
  2. 從NSArray看類簇
  3. 創(chuàng)建者模式-建造者模式(The Builder Pattern)
  4. 簡單工廠模式(Simple Factory Pattern)
  5. 工廠方法模式(Factory Method Pattern)
  6. 抽象工廠模式(Abstract Factory)
  7. Swift中編寫單例的正確方式
本站僅提供存儲服務,所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
iOS開發(fā)之int,NSInteger,NSUInteger,NSNumber的使用
今年網(wǎng)易最后一道C++筆試題是考了這樣一道題目
Python:Python學習總結(jié)
CEF和JS交互
泛型委托
編程風格
更多類似文章 >>
生活服務
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服