How does the builder sample work?
The builder sample could be applied in a number of methods, however that actually does not issues in case you perceive the primary aim of the sample:
The intent of the Builder design sample is to separate the development of a posh object from its illustration.
So when you have an object with numerous properties, you need to disguise the complexity of the initialization course of, you may write a builder and assemble the item by that. It may be so simple as a construct methodology or an exterior class that controls your entire building course of. All of it will depend on the given surroundings. 🏗
That is sufficient idea for now, let’s have a look at the builder sample in motion utilizing dummy, however real-world examples and the highly effective Swift programming language! 💪
Easy emitter builder
I imagine that SKEmitterNode is kind of a pleasant instance. If you wish to create customized emitters and set properties programmatically – often for a SpriteKit sport – an emitter builder class like this might be an affordable resolution. 👾
class EmitterBuilder {
func construct() -> 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 = .crimson
emitter.particleColorBlendFactor = 1
return emitter
}
}
EmitterBuilder().construct()
Easy theme builder
Let’s transfer away from gaming and picture that you’re making a theme engine in your UIKit utility which has many customized fonts, colours, and many others. a builder might be helpful to assemble standalone themes. 🔨
struct Theme {
let textColor: UIColor?
let backgroundColor: UIColor?
}
class ThemeBuilder {
enum Model {
case mild
case darkish
}
func construct(_ type: Model) -> Theme {
swap type {
case .mild:
return Theme(textColor: .black, backgroundColor: .white)
case .darkish:
return Theme(textColor: .white, backgroundColor: .black)
}
}
}
let builder = ThemeBuilder()
let mild = builder.construct(.mild)
let darkish = builder.construct(.darkish)
"Chained" URL builder
With this method you may configure your object by numerous strategies and each single considered one of them will return the identical builder object. This method you may chain the configuration and as a final step construct the closing product. ⛓
class URLBuilder {
non-public var parts: URLComponents
init() {
self.parts = URLComponents()
}
func set(scheme: String) -> URLBuilder {
self.parts.scheme = scheme
return self
}
func set(host: String) -> URLBuilder {
self.parts.host = host
return self
}
func set(port: Int) -> URLBuilder {
self.parts.port = port
return self
}
func set(path: String) -> URLBuilder {
var path = path
if !path.hasPrefix("/") {
path = "/" + path
}
self.parts.path = path
return self
}
func addQueryItem(title: String, worth: String) -> URLBuilder {
if self.parts.queryItems == nil {
self.parts.queryItems = []
}
self.parts.queryItems?.append(URLQueryItem(title: title, worth: worth))
return self
}
func construct() -> URL? {
return self.parts.url
}
}
let url = URLBuilder()
.set(scheme: "https")
.set(host: "localhost")
.set(path: "api/v1")
.addQueryItem(title: "type", worth: "title")
.addQueryItem(title: "order", worth: "asc")
.construct()
The builder sample with a director
Let’s meet the director object. Because it looks as if this little factor decouples the builder from the precise configuration half. So as an example you may make a sport with circles, however in a while in case you change your thoughts and you want to make use of squares, that is comparatively simple. You simply should create a brand new builder, and every thing else could be the identical. 🎬
protocol NodeBuilder {
var title: String { get set }
var coloration: SKColor { get set }
var dimension: CGFloat { get set }
func construct() -> SKShapeNode
}
protocol NodeDirector {
var builder: NodeBuilder { get set }
func construct() -> SKShapeNode
}
class CircleNodeBuilder: NodeBuilder {
var title: String = ""
var coloration: SKColor = .clear
var dimension: CGFloat = 0
func construct() -> SKShapeNode {
let node = SKShapeNode(circleOfRadius: self.dimension)
node.title = self.title
node.fillColor = self.coloration
return node
}
}
class PlayerNodeDirector: NodeDirector {
var builder: NodeBuilder
init(builder: NodeBuilder) {
self.builder = builder
}
func construct() -> SKShapeNode {
self.builder.title = "Hiya"
self.builder.dimension = 32
self.builder.coloration = .crimson
return self.builder.construct()
}
}
let builder = CircleNodeBuilder()
let director = PlayerNodeDirector(builder: builder)
let participant = director.construct()
Block primarily based builders
A extra swifty method could be using blocks as an alternative of builder courses to configure objects. After all we may argue on if that is nonetheless a builder sample or not… 😛
extension UILabel {
static func construct(block: ((UILabel) -> Void)) -> UILabel {
let label = UILabel(body: .zero)
block(label)
return label
}
}
let label = UILabel.construct { label in
label.translatesAutoresizingMaskIntoConstraints = false
label.textual content = "Hiya wold!"
label.font = UIFont.systemFont(ofSize: 12)
}
Please word that the builder implementation can differ on the precise use case. Generally a builder is mixed with factories. So far as I can see nearly everybody interpreted it another way, however I do not assume that is an issue. Design patterns are well-made pointers, however typically you must cross the road.