Builder Design pattern -swift- Examples
The Builder design pattern is a creational design pattern that allows you to create complex objects step by step, by separating the object construction process from its representation. In Swift, you can implement the Builder pattern in the following way:
By using the builder pattern, you can create complex objects without exposing their construction details to the client. Additionally, the builder pattern allows you to create different variations of the same object by using different builder classes.
The Builder design pattern provides several benefits:
Separation of concerns: The pattern separates the construction of a complex object from its representation. This means that the object construction logic is separated from the object representation, allowing both to vary independently.
Encapsulation: The pattern encapsulates the object construction logic, providing a single point of control. This makes it easy to modify the construction process without affecting the client code.
Flexibility: The pattern allows for the creation of different object configurations without duplicating code. This is because the object construction logic is implemented in the builder class, and clients can use different builders to create different object configurations.
Readability: The pattern makes code more readable and maintainable. It does this by using descriptive method names in the builder class that make it clear what each method does.
Reusability: The pattern promotes code reuse by allowing clients to reuse the same builder to create different object configurations. This can save time and effort by avoiding the need to write new code for each configuration.
Here are different examples to learn Builder Design Pattern:-
class Product {
var propertyA: String?
var propertyB: Int?
var propertyC: Bool?
func doSomething() {
// ...
}
}
class ProductBuilder {
private var product = Product()
func setPropertyA(_ propertyA: String) -> Self {
product.propertyA = propertyA
return self
}
func setPropertyB(_ propertyB: Int) -> Self {
product.propertyB = propertyB
return self
}
func setPropertyC(_ propertyC: Bool) -> Self {
product.propertyC = propertyC
return self
}
func build() -> Product {
return product
}
}
let product = ProductBuilder()
.setPropertyA("Value for property A")
.setPropertyB(42)
.setPropertyC(true)
.build()
class URLBuilder {
private var scheme: String?
private var host: String?
private var path: String?
private var queryItems: [URLQueryItem] = []
func setScheme(_ scheme: String) -> Self {
self.scheme = scheme
return self
}
func setHost(_ host: String) -> Self {
self.host = host
return self
}
func setPath(_ path: String) -> Self {
self.path = path
return self
}
func addQueryItem(name: String, value: String) -> Self {
let queryItem = URLQueryItem(name: name, value: value)
queryItems.append(queryItem)
return self
}
func build() -> URL? {
var components = URLComponents()
components.scheme = scheme
components.host = host
components.path = path
components.queryItems = queryItems.isEmpty ? nil : queryItems
return components.url
}
}
let url = URLBuilder()
.setScheme("https")
.setHost("example.com")
.setPath("/path/to/resource")
.addQueryItem(name: "param1", value: "value1")
.addQueryItem(name: "param2", value: "value2")
.build()
print(url) // https://example.com/path/to/resource?param1=value1¶m2=value2
class Pizza {
var crust: String?
var sauce: String?
var toppings: [String] = []
}
protocol PizzaBuilder {
func setCrust(_ crust: String) -> Self
func setSauce(_ sauce: String) -> Self
func addTopping(_ topping: String) -> Self
func build() -> Pizza
}
class MargheritaPizzaBuilder: PizzaBuilder {
private var pizza = Pizza()
func setCrust(_ crust: String) -> Self {
pizza.crust = crust
return self
}
func setSauce(_ sauce: String) -> Self {
pizza.sauce = sauce
return self
}
func addTopping(_ topping: String) -> Self {
pizza.toppings.append(topping)
return self
}
func build() -> Pizza {
pizza.crust = pizza.crust ?? "thin"
pizza.sauce = pizza.sauce ?? "tomato"
return pizza
}
}
class MeatLoversPizzaBuilder: PizzaBuilder {
private var pizza = Pizza()
func setCrust(_ crust: String) -> Self {
pizza.crust = crust
return self
}
func setSauce(_ sauce: String) -> Self {
pizza.sauce = sauce
return self
}
func addTopping(_ topping: String) -> Self {
pizza.toppings.append(topping)
return self
}
func build() -> Pizza {
pizza.crust = pizza.crust ?? "thick"
pizza.sauce = pizza.sauce ?? "barbecue"
pizza.toppings += ["bacon", "sausage", "ham"]
return pizza
}
}
class PizzaDirector {
func makePizza(_ builder: PizzaBuilder) -> Pizza {
return builder.setCrust("thin").setSauce("tomato").addTopping("cheese").build()
}
}
let margheritaBuilder = MargheritaPizzaBuilder()
let margheritaPizza = margheritaBuilder.setCrust("thin").setSauce("tomato").addTopping("cheese").addTopping("basil").build()
print(margheritaPizza) // Pizza(crust: "thin", sauce: "tomato", toppings: ["cheese", "basil"])
let meatLoversBuilder = MeatLoversPizzaBuilder()
let meatLoversPizza = meatLoversBuilder.setCrust("thick").addTopping("onion").addTopping("mushroom").build()
print(meatLoversPizza) // Pizza(crust: "thick", sauce: "barbecue", toppings: ["onion", "mushroom", "bacon", "sausage", "ham"])
class ViewBuilder {
private let view: UIView
init(view: UIView) {
self.view = view
}
func set(backgroundColor: UIColor) -> Self {
view.backgroundColor = backgroundColor
return self
}
func set(frame: CGRect) -> Self {
view.frame = frame
return self
}
func set(cornerRadius: CGFloat) -> Self {
view.layer.cornerRadius = cornerRadius
return self
}
func set(borderColor: UIColor) -> Self {
view.layer.borderColor = borderColor.cgColor
return self
}
func set(borderWidth: CGFloat) -> Self {
view.layer.borderWidth = borderWidth
return self
}
func build() -> UIView {
return view
}
}
let myView = ViewBuilder(view: UIView())
.set(backgroundColor: .blue)
.set(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
.set(cornerRadius: 10)
.set(borderColor: .black)
.set(borderWidth: 2)
.build()
In this example, we create a new UIView instance and pass it to the ViewBuilder. We then use the builder's methods to set various properties of the view, such as background color, frame, and border. Finally, we call build() to get the completed UIView.
class ViewControllerBuilder {
private let viewController: UIViewController
init(viewController: UIViewController) {
self.viewController = viewController
}
func set(title: String) -> Self {
viewController.title = title
return self
}
func set(navigationItemTitle: String) -> Self {
viewController.navigationItem.title = navigationItemTitle
return self
}
func set(tabBarItemTitle: String, image: UIImage?) -> Self {
viewController.tabBarItem = UITabBarItem(title: tabBarItemTitle, image: image, selectedImage: nil)
return self
}
func build() -> UIViewController {
return viewController
}
}
let myViewController = ViewControllerBuilder(viewController: UIViewController())
.set(title: "My Title")
.set(navigationItemTitle: "Navigation Item Title")
.set(tabBarItemTitle: "Tab Bar Item Title", image: UIImage(named: "myImage"))
.build()
enum ChatMessageType {
case text
case image
// add more types as needed
}
protocol ChatMessage {
var type: ChatMessageType { get }
var sender: String { get }
var timestamp: Date { get }
}
class ChatMessageBuilder {
private var type: ChatMessageType = .text
private var sender: String = ""
private var timestamp: Date = Date()
func set(type: ChatMessageType) -> Self {
self.type = type
return self
}
func set(sender: String) -> Self {
self.sender = sender
return self
}
func set(timestamp: Date) -> Self {
self.timestamp = timestamp
return self
}
func build() -> ChatMessage {
switch type {
case .text:
return TextChatMessage(sender: sender, timestamp: timestamp)
case .image:
return ImageChatMessage(sender: sender, timestamp: timestamp)
}
}
}
class TextChatMessage: ChatMessage {
let type: ChatMessageType = .text
let sender: String
let timestamp: Date
let text: String
init(sender: String, timestamp: Date, text: String = "") {
self.sender = sender
self.timestamp = timestamp
self.text = text
}
}
class ImageChatMessage: ChatMessage {
let type: ChatMessageType = .image
let sender: String
let timestamp: Date
let image: UIImage
init(sender: String, timestamp: Date, image: UIImage = UIImage()) {
self.sender = sender
self.timestamp = timestamp
self.image = image
}
}
let myTextMessage = ChatMessageBuilder()
.set(type: .text)
.set(sender: "John")
.set(timestamp: Date())
.build() as! TextChatMessage
myTextMessage.text = "Hello, world!"
let myImageMessage = ChatMessageBuilder()
.set(type: .image)
.set(sender: "Jane")
.set(timestamp: Date())
.build() as! ImageChatMessage
myImageMessage.image = UIImage(named: "myImage")
import Foundation
enum CalendarEventType {
case meeting
case reminder
// add more types as needed
}
protocol CalendarEvent {
var title: String { get }
var startDate: Date { get }
var endDate: Date { get }
var eventType: CalendarEventType { get }
}
class CalendarEventBuilder {
private var title: String = ""
private var startDate: Date = Date()
private var endDate: Date = Date()
private var eventType: CalendarEventType = .meeting
func setTitle(_ title: String) -> Self {
self.title = title
return self
}
func setStartDate(_ startDate: Date) -> Self {
self.startDate = startDate
return self
}
func setEndDate(_ endDate: Date) -> Self {
self.endDate = endDate
return self
}
func setEventType(_ eventType: CalendarEventType) -> Self {
self.eventType = eventType
return self
}
func build() -> CalendarEvent {
switch eventType {
case .meeting:
return Meeting(title: title, startDate: startDate, endDate: endDate)
case .reminder:
return Reminder(title: title, startDate: startDate, endDate: endDate)
}
}
}
class Meeting: CalendarEvent {
let title: String
let startDate: Date
let endDate: Date
let eventType: CalendarEventType = .meeting
init(title: String, startDate: Date, endDate: Date) {
self.title = title
self.startDate = startDate
self.endDate = endDate
}
}
class Reminder: CalendarEvent {
let title: String
let startDate: Date
let endDate: Date
let eventType: CalendarEventType = .reminder
init(title: String, startDate: Date, endDate: Date) {
self.title = title
self.startDate = startDate
self.endDate = endDate
}
}
let myMeetingEvent = CalendarEventBuilder()
.setTitle("Project Meeting")
.setStartDate(Date())
.setEndDate(Date().addingTimeInterval(3600)) // 1 hour after start date
.setEventType(.meeting)
.build() as! Meeting
let myReminderEvent = CalendarEventBuilder()
.setTitle("Buy groceries")
.setStartDate
import UIKit
protocol TableViewBuilder {
var numberOfRows: Int { get set }
var cellReuseIdentifier: String { get set }
var cellClass: AnyClass { get set }
var delegate: UITableViewDelegate? { get set }
var dataSource: UITableViewDataSource? { get set }
func build() -> UITableView
}
class SimpleTableViewBuilder: TableViewBuilder {
var numberOfRows = 0
var cellReuseIdentifier = ""
var cellClass: AnyClass = UITableViewCell.self
var delegate: UITableViewDelegate?
var dataSource: UITableViewDataSource?
func build() -> UITableView {
let tableView = UITableView()
tableView.delegate = delegate
tableView.dataSource = dataSource
tableView.register(cellClass, forCellReuseIdentifier: cellReuseIdentifier)
return tableView
}
}
let tableView = SimpleTableViewBuilder()
.setNumberOfRows(10)
.setCellReuseIdentifier("MyCellReuseIdentifier")
.setCellClass(MyTableViewCell.self)
.setDelegate(self)
.setDataSource(self)
.build()
import UIKit
protocol NavigationControllerBuilder {
var rootViewController: UIViewController? { get set }
var navigationBarHidden: Bool { get set }
var navigationBarTintColor: UIColor? { get set }
var navigationBarTitleTextAttributes: [NSAttributedString.Key: Any]? { get set }
var delegate: UINavigationControllerDelegate? { get set }
func build() -> UINavigationController
}
class SimpleNavigationControllerBuilder: NavigationControllerBuilder {
var rootViewController: UIViewController?
var navigationBarHidden = false
var navigationBarTintColor: UIColor?
var navigationBarTitleTextAttributes: [NSAttributedString.Key: Any]?
var delegate: UINavigationControllerDelegate?
func build() -> UINavigationController {
let navigationController = UINavigationController(rootViewController: rootViewController ?? UIViewController())
navigationController.isNavigationBarHidden = navigationBarHidden
navigationController.navigationBar.tintColor = navigationBarTintColor
navigationController.navigationBar.titleTextAttributes = navigationBarTitleTextAttributes
navigationController.delegate = delegate
return navigationController
}
}
let viewController = MyViewController()
let navigationController = SimpleNavigationControllerBuilder()
.setRootViewController(viewController)
.setNavigationBarHidden(false)
.setNavigationBarTintColor(.blue)
.setNavigationBarTitleTextAttributes([.foregroundColor: UIColor.white])
.setDelegate(self)
.build()
class Exam {
let title: String
let durationInMinutes: Int
let questions: [Question]
init(title: String, durationInMinutes: Int, questions: [Question]) {
self.title = title
self.durationInMinutes = durationInMinutes
self.questions = questions
}
class Builder {
var title: String = ""
var durationInMinutes: Int = 0
var questions: [Question] = []
func setTitle(_ title: String) -> Builder {
self.title = title
return self
}
func setDurationInMinutes(_ durationInMinutes: Int) -> Builder {
self.durationInMinutes = durationInMinutes
return self
}
func setQuestions(_ questions: [Question]) -> Builder {
self.questions = questions
return self
}
func build() -> Exam {
return Exam(title: title, durationInMinutes: durationInMinutes, questions: questions)
}
}
}
class Question {
let text: String
let answers: [String]
let correctAnswerIndex: Int
init(text: String, answers: [String], correctAnswerIndex: Int) {
self.text = text
self.answers = answers
self.correctAnswerIndex = correctAnswerIndex
}
}
let exam = Exam.Builder()
.setTitle("Math Exam")
.setDurationInMinutes(60)
.setQuestions([
Question(text: "What is 2 + 2?", answers: ["3", "4", "5"], correctAnswerIndex: 1),
Question(text: "What is the square root of 16?", answers: ["2", "4", "8"], correctAnswerIndex: 1),
Question(text: "What is the value of pi?", answers: ["3.14", "2.72", "1.61"], correctAnswerIndex: 0)
])
.build()
class Slider {
var minValue: Float
var maxValue: Float
var currentValue: Float
init(minValue: Float, maxValue: Float, currentValue: Float) {
self.minValue = minValue
self.maxValue = maxValue
self.currentValue = currentValue
}
class Builder {
private var minValue: Float = 0
private var maxValue: Float = 1
private var currentValue: Float = 0
func setMinValue(_ minValue: Float) -> Builder {
self.minValue = minValue
return self
}
func setMaxValue(_ maxValue: Float) -> Builder {
self.maxValue = maxValue
return self
}
func setCurrentValue(_ currentValue: Float) -> Builder {
self.currentValue = currentValue
return self
}
func build() -> Slider {
return Slider(minValue: minValue, maxValue: maxValue, currentValue: currentValue)
}
}
}
let slider = Slider.Builder()
.setMinValue(0)
.setMaxValue(100)
.setCurrentValue(50)
.build()