設計模式 Swift(基本介紹)

閱讀大話設計模式心得,使用 Swift

類別

具有相同屬性和功能的物件的抽象集合

1
2
3
4
5
class Cat {
func Shout() -> String{
return "Meow"
}
}

建立真實的物件 -> 實體

1
2
let cat = Cat()
print(cat.Shout())

建構式

初始化實體物件時,傳入參數來初始化實體屬性

1
2
3
4
5
6
7
8
9
10
11
12
13
class Cat {
private var name: String
init(name: String) {
self.name = name
}

func Shout() -> String{
return "My name is \(self.name), Meow"
}
}
let cat = Cat(name:"MiMi")
print(cat.Shout())
print(cat.name)

More info: Access Control

重載(Overload)

建立多個同樣方法名稱,但參數類型或數量不同

優點在於可以不改動原始方法,但擴展原始方法的能力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Cat {
private var name: String
// 建立兩個同名 init 方法,但參數不同
init(name: String) {
self.name = name
}

init() {
self.name = "No name"
}

func Shout() -> String{
return "My name is \(self.name), Meow"
}
}
let cat1 = Cat(name:"MiMi")
let cat2 = Cat()
print(cat1.Shout())
print(cat2.Shout())

屬性

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
class Cat {
private var name : String
private var shoutNum = 3
init(name: String) {
self.name = name
}

init() {
self.name = "No name"
}

func Shout() -> String{
var res = ""
for i in 0 ..< shoutNum {
res = res + "Meow "
}
return "My name is \(self.name), \(res)"
}

var ShoutNum: Int {
get {
return self.shoutNum
}
set {
if newValue <= 10 {
shoutNum = newValue
}else{
shoutNum = 10
}
}
}
}
let cat = Cat(name:"MiMi")
cat.shoutNum = 5
print(cat.Shout())

封裝

上述 Cat 類別就是實現封裝。

封裝可以減少耦合,類別內部可以自由修改不影響其他類別,類別具有清晰的對外界面

繼承

如果此時想做一個 Dog 類別,可以複製一份 Cat 改成 Dog ,但是程式碼會大量重複

這時可以使用繼承,把相同的程式碼抽離出來

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
48
49
50
51
52
class Animal {
var name : String
init(name: String) {
self.name = name
}

init() {
self.name = "No name"
}

var shoutNum = 3
var ShoutNum: Int {
get {
return self.shoutNum
}
set {
shoutNum = newValue
}
}

func Shout() -> String{
var res = ""
for _ in 0 ..< shoutNum {
res = res + "Meow "
}
return "My name is \(self.name), \(res)"
}
}

class Cat : Animal {
override init() { super.init() }
override init(name: String) { super.init(name:name) }
override func Shout() -> String{
var res = ""
for _ in 0 ..< shoutNum {
res = res + "Meow "
}
return "My name is \(self.name), \(res)"
}
}

class Dog : Animal {
override init() { super.init() }
override init(name: String) { super.init(name:name) }
override func Shout() -> String{
var res = ""
for _ in 0 ..< shoutNum {
res = res + "Wang "
}
return "My name is \(self.name), \(res)"
}
}

繼承缺點:父類別變動 -> 子類別也會跟著變,破壞封裝,增加父類子類之間的耦合

繼承時機: 兩者之間是 is-a 關係可以,has-a 關係不行

多型

假設目前需要儲存動物陣列,並依序發出叫聲

多型表示:不同物件但是可以執行相同的動作,但會透過他們各自實現的程式碼來執行

1
2
3
4
5
6
7
8
9
10
var array:[Animal] = []
array.append(Cat(name:"小花"))
array.append(Dog(name:"小黃"))
array.append(Dog(name:"小黑"))
array.append(Cat(name:"小毛"))
array.append(Cat(name:"咪咪"))

for idx in 0..<array.count {
print("array[\(idx)] = \(array[idx].Shout())")
}

重構

如果再增加其他動物,如牛跟羊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Ox : Animal {
override init() { super.init() }
override init(name: String) { super.init(name:name) }
override func Shout() -> String{
var res = ""
for _ in 0 ..< shoutNum {
res = res + "Moo "
}
return "My name is \(self.name), \(res)"
}
}

class Sheep : Animal {
override init() { super.init() }
override init(name: String) { super.init(name:name) }
override func Shout() -> String{
var res = ""
for _ in 0 ..< shoutNum {
res = res + "Mee "
}
return "My name is \(self.name), \(res)"
}
}

現在會發現,Shout() 還是會重複,所以可以把 Shout() 移動 Animal

然後再建立一個新方法 getShoutSound(),之後子類別覆寫此方法

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
48
49
50
class Animal {
var name : String
init(name: String) { self.name = name }
init() { self.name = "No name" }

var shoutNum = 3
var ShoutNum: Int {
get {
return self.shoutNum
}
set {
shoutNum = newValue
}
}

func Shout() -> String{
var res = ""
for _ in 0 ..< shoutNum {
res = res + getShoutSound()
}
return "My name is \(self.name), \(res)"
}

func getShoutSound() -> String{
return ""
}
}

class Cat : Animal {
override init() { super.init() }
override init(name: String) { super.init(name:name) }
override func getShoutSound() -> String{ return "Meow " }
}

class Dog : Animal {
override init() { super.init() }
override init(name: String) { super.init(name:name) }
override func getShoutSound() -> String{ return "Wang " }
}
class Ox : Animal {
override init() { super.init() }
override init(name: String) { super.init(name:name) }
override func getShoutSound() -> String{ return "Moo " }
}

class Sheep : Animal {
override init() { super.init() }
override init(name: String) { super.init(name:name) }
override func getShoutSound() -> String{ return "Mee " }
}

抽象化

這裡的 Animal 是一種抽象的意思,因為 Animal 不應該可以實體化(var animal = new Animal())

所以我們可以把實體化沒意義的類別改為抽象類別,同理 getShoutSound() 方法體沒意義也可改為抽象方法

在 Java 中使用 abstract 作為關鍵字,Swift 沒有 abstract,可用 Protocol + extension 取代

我們要記得,抽象類別是用來繼承的

More info: Extensions

More info: Protocols

介面

封裝特定功能的一個集合,Swift 中的 protocol 相當於 Java 中的 Interface

1
2
3
4
5
6
7
8
9
10
protocol PChange {
func ChangeThing(thing:String) -> String
}

class MachineCat : Cat,PChange {
override init() { super.init() }
override init(name: String) { super.init(name:name) }
override func getShoutSound() -> String{ return "Meow " }
func ChangeThing(thing:String) -> String { return super.Shout()+"MachineCat" }
}

抽象類別 vs 介面

1
2
3
4
5
6
7
類別是對物件的抽象
抽象類別是對類別的抽象
介面是對行為的抽象


抽象類別往往是重構而來(類別之間找出相同特性抽離出來,形成父類別)
介面是跨越不同種類物件,但是希望都能實現的方法

裝箱和封箱

boxing & unboxing 會消耗資源和時間

1
2
3
4
5
int i = 123
object o = (object) i; //boxing

o = 123
i = (int) o; //unboxing

封裝特定功能的一個集合,Swift 中的 protocol 相當於 Java 中的 Interface

1
2
3
4
5
6
7
8
9
10
protocol PChange {
func ChangeThing(thing:String) -> String
}

class MachineCat : Cat,PChange {
override init() { super.init() }
override init(name: String) { super.init(name:name) }
override func getShoutSound() -> String{ return "Meow " }
func ChangeThing(thing:String) -> String { return super.Shout()+"MachineCat" }
}