0%

Swift生成器模式

了解如何在Swift中实现生成器模式,以隐藏创建具有许多单独属性的对象的复杂性。

        在 iOS 开发中,会使用到很多设计模式,生成器模式 也叫构建器模式,我们也会经常用到。在以下的行文过程中均使用生成器模式来表述。

        生成器模式(英:Builder Pattern)是一种设计模式,又名:建造模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。


生成器模式如何工作?

        生成器模式可以通过多种方式实现,但是如果你了解该模式的主要目标,那实际上就没有关系:

Builder设计模式的目的是将复杂对象的构造与其表示分开。

        因此,如果你有一个具有很多属性的对象,想隐藏初始化过程的复杂性,可以编写一个生成器并通过该生成器构造该对象。 它可以像控制整个构建过程的构建方法或外部类一样简单。 这完全取决于给定的环境。 🏗

        到目前为止,我们对该设计模式有一个详细的认知,让我们来看一下使用实例和强大的 Swift 编程语言的生成器模式! 💪


简单的 Emitter 生成器

         SKEmitterNode 是一个很好的例子。 如果要创建自定义 Emitter 并以编程方式设置属性(通常用于 SpriteKit 游戏),则像这样的 Emitter 生成器类可能是一个合理的解决方案。 👾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class EmitterBuilder {

func build() -> SKEmitterNode {
let emitter = SKEmitterNode()
emitter.particleTexture = SKTexture(imageNamed: "MyTexture")
emitter.particleBirthRate = 100
emitter.particleLifetime = 60
emitter.particlePositionRange = CGVector(dx: 100, dy: 100)
emitter.particleSpeed = 10
emitter.particleColor = .red
emitter.particleColorBlendFactor = 1
return emitter
}
}

EmitterBuilder().build()

简单的 theme 生成器

         让我们远离游戏,想象一下你正在为UIKit应用程序创建一个主题引擎,该引擎具有许多自定义字体,颜色等。生成器对于构造独立主题可能很有用。 🔨

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
struct Theme {
let textColor: UIColor?
let backgroundColor: UIColor?
}

class ThemeBuilder {

enum Style {
case light
case dark
}

func build(_ style: Style) -> Theme {
switch style {
case .light:
return Theme(textColor: .black, backgroundColor: .white)
case .dark:
return Theme(textColor: .white, backgroundColor: .black)
}
}
}

let builder = ThemeBuilder()
let light = builder.build(.light)
let dark = builder.build(.dark)

“Chained” URL构建器

        使用这种方法,你可以通过多种方法配置对象,并且每个方法都将返回相同的生成器对象。 这样,您可以链接配置,并在最后一步构建最终对象实例。 ⛓

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 URLBuilder {

private var components: URLComponents

init() {
self.components = URLComponents()
}

func set(scheme: String) -> URLBuilder {
self.components.scheme = scheme
return self
}

func set(host: String) -> URLBuilder {
self.components.host = host
return self
}

func set(port: Int) -> URLBuilder {
self.components.port = port
return self
}

func set(path: String) -> URLBuilder {
var path = path
if !path.hasPrefix("/") {
path = "/" + path
}
self.components.path = path
return self
}

func addQueryItem(name: String, value: String) -> URLBuilder {
if self.components.queryItems == nil {
self.components.queryItems = []
}
self.components.queryItems?.append(URLQueryItem(name: name, value: value))
return self
}

func build() -> URL? {
return self.components.url
}
}

let url = URLBuilder()
.set(scheme: "https")
.set(host: "localhost")
.set(path: "api/v1")
.addQueryItem(name: "sort", value: "name")
.addQueryItem(name: "order", value: "asc")
.build()

director 类的生成器模式

         让我们认识一下 director 实例。 使构建器与确切的配置部分解耦。 因此,举例来说,你可以制作带有圆圈的游戏,但是后来如果你改变主意并想使用正方形,那相对容易。 你只需要创建一个新的生成器,其他所有内容都可以相同。 🎬

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
protocol NodeBuilder {
var name: String { get set }
var color: SKColor { get set }
var size: CGFloat { get set }

func build() -> SKShapeNode
}

protocol NodeDirector {
var builder: NodeBuilder { get set }

func build() -> SKShapeNode
}

class CircleNodeBuilder: NodeBuilder {
var name: String = ""
var color: SKColor = .clear
var size: CGFloat = 0

func build() -> SKShapeNode {
let node = SKShapeNode(circleOfRadius: self.size)
node.name = self.name
node.fillColor = self.color
return node
}
}

class PlayerNodeDirector: NodeDirector {

var builder: NodeBuilder

init(builder: NodeBuilder) {
self.builder = builder
}

func build() -> SKShapeNode {
self.builder.name = "Hello"
self.builder.size = 32
self.builder.color = .red
return self.builder.build()
}
}

let builder = CircleNodeBuilder()
let director = PlayerNodeDirector(builder: builder)
let player = director.build()

基于Block的构建器

         一种更快捷的方法是使用 Block 而不是生成器类来配置对象。 当然,我们可以争论这是否仍然是生成器模式…😛

1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension UILabel {

static func build(block: ((UILabel) -> Void)) -> UILabel {
let label = UILabel(frame: .zero)
block(label)
return label
}
}

let label = UILabel.build { label in
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello wold!"
label.font = UIFont.systemFont(ofSize: 12)
}

         请注意,生成器的实现可能会因具体实例而异。 有时,生成器模式与工厂模式结合在一起。 对此,几乎每个人都以不同的方式解释它,但是我认为这不是问题。 设计模式是精心设计的准则,但有时你必须这么做。

坚持原创技术分享,您的支持将鼓励我继续创作!

欢迎关注我的其它发布渠道