更多内容,欢迎关注公众号:Swift花园
喜欢文章?不如来点赞关注吧
实现 builder 模式,隐藏创建拥有大量属性的对象的复杂性。
builder 模式如何工作?
builder 模式可以采用多种方式实现,不过如果你明白这种模式的主要目标,那么用哪一种方式都无所谓。
Builder 设计模式的意图在于从复杂对象的表达中分离出构造对象的部分。
因此,如果你的对象有很多属性,而你想要隐藏构造过程的复杂性,那么你可以写一个 builder,通过它来构造你的对象。这个 builder 可以简单如一个 build 方法,也可以复杂如一个控制全部构建过程的外部类。这些都是由环境决定的。🏗
理论到此如此,让我们用 builder 模式来实战一番吧! 💪
简单发射器 builder
我认为 SKEmitterNode 是一个很不错的粒子。如果你打算创建自定义发射器并且通过程序设置属性,通过是在 SpriteKit 游戏当中。那么像下面这样的一个发射器 builder 类就是一种很合理的解决方案。👾
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()
简单主题 builder
想象一下你正在为你的 UIKit 应用设计一个主题引擎,它有许多自定义字体、颜色等。构造标准主题时一个 builder 会很有用。 🔨
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)
"链式" URL builder
利用这种方式,你可以用各种方法配置对象,每一个方法都会返回同一个 builder 对象,然后在最后一步构建最终产品。⛓
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 的 builder 模式
让我们来见一见director对象,它的功能是将 builder 和配置部分的代码解耦。举个例子,你本来想创建一个圆形,可是忽然改主意要换成一个正方形。这可以很容易实现。只需要创建一个新的 builder,其他的都不变。🎬
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的builders
一种更 Swift 的方法可以使用 block 来代替 builder 类来配置对象。当然,可能有人要问这样还算不算 builder 模式... 😛
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)
}注意,builder 的实现根据特定的用例可能有所差别。有的时候 builder 会跟工厂结合。每个人对这个模式的解读都不同,但并不是问题。设计模式是制作精良的准则,但有的时候规则是可以被打破的。
我的公众号
这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~
