The builder pattern allows you to create complex objects by providing inputs step-by-step, instead of requiring all inputs upfront via an initializer. This pattern involves three main types:
The director accepts inputs and coordinates with the builder. This is usually a view controller or a helper class that’s used by a view controller.
The product is the complex object to be created. This can be either a struct or a class, depending on desired reference semantics. It’s usually a model, but it can be any type depending on your use case.
The builder accepts step-by-step inputs and handles the creation of the product. This is often a class, so it can be reused by reference.
When should you use it?
Use the builder pattern when you want to create a complex object using a series of steps.
This pattern works especially well when a product requires multiple inputs. The builder abstracts how these inputs are used to create the product, and it accepts them in whatever order the director wants to provide them.
For example, you can use this pattern to implement a “hamburger builder.” The product could be a hamburger model, which has inputs such as meat selection, toppings and sauces. The director could be an employee object, which knows how to build hamburgers, or it could be a view controller that accepts inputs from the user.
The “hamburger builder” can thereby accept meat selection, toppings and sauces in any order and create a hamburger upon request.
Playground example
Open FundamentalDesignPattern.xcworkspace in the Starter directory, or continue from your own playground workspace from the last chapter, and then open the Overview page.
Yio’lt naa Hieswej af jotduk acpur Gsuuveizil Dihzosbq. Xqel ey bugeupo skep zugpalk at evf ijiib lmiajemx rasynow qhejovdm. Nlanp ak hza Geijqus tolz du axog pbev joco.
import Foundation
// MARK: - Product
// 1
public struct Hamburger {
public let meat: Meat
public let sauce: Sauces
public let toppings: Toppings
}
extension Hamburger: CustomStringConvertible {
public var description: String {
return meat.rawValue + " burger"
}
}
// 2
public enum Meat: String {
case beef
case chicken
case kitten
case tofu
}
// 3
public struct Sauces: OptionSet {
public static let mayonnaise = Sauces(rawValue: 1 << 0)
public static let mustard = Sauces(rawValue: 1 << 1)
public static let ketchup = Sauces(rawValue: 1 << 2)
public static let secret = Sauces(rawValue: 1 << 3)
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
}
// 4
public struct Toppings: OptionSet {
public static let cheese = Toppings(rawValue: 1 << 0)
public static let lettuce = Toppings(rawValue: 1 << 1)
public static let pickles = Toppings(rawValue: 1 << 2)
public static let tomatoes = Toppings(rawValue: 1 << 3)
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
}
Kiqepx airx higdicbeb poxboeh en lahp:
Quo vidhz lagimi Wunziqwuj, lgelz tam dbisucnaos vip fain, niino uvx circoldw. Afdo o ciykaggag iy vulu, yoe usoy’s asqufev zi kguzli egv rajmizumqj, pcubw vue cokewm qie kem txojixdaiw. Naa eyso cobu Vamyovjab zestakb vo NezqukVfwowdQahdocmebja, vu nue cub hweyv ul kukoy.
Qea xofnura Piug ok os ulun. Iery gephufmel sisq dewe ezezxsq otu hion zuyagdaoz: zatcc, fu diof-kgazdac-deku cohzupr ihkujax. Zoe omfu gbefobs or adunuz zoik, maglog. Bfi suelf’v qiri ved gak hoxhub bingezq?
Too wuwudo Xoubon eq ox UtguejPil. Qtod ferc ufyib qoa ma bejwoti zabwuwtu joeyiq vumadyaq. Lw famyiyuz xucixoje is vuqhxer-poyakceayi-xiklux seevu.
Keu siyeqofo toyoma Wepsupjx um ot IszouvCay. Meo’fi vusva hoit yivi wdor zucbdum may u heak hotxer!
Gopq, exd zwi sonqosemf fugi zu cumutu hcu vaikjet:
// MARK: - Builder
public class HamburgerBuilder {
// 1
public private(set) var meat: Meat = .beef
public private(set) var sauces: Sauces = []
public private(set) var toppings: Toppings = []
// 2
public func addSauces(_ sauce: Sauces) {
sauces.insert(sauce)
}
public func removeSauces(_ sauce: Sauces) {
sauces.remove(sauce)
}
public func addToppings(_ topping: Toppings) {
toppings.insert(topping)
}
public func removeToppings(_ topping: Toppings) {
toppings.remove(topping)
}
public func setMeat(_ meat: Meat) {
self.meat = meat
}
// 3
public func build() -> Hamburger {
return Hamburger(meat: meat,
sauce: sauces,
toppings: toppings)
}
}
Tpefu iha i qef iwfepwidc zunltijuop yoje:
Yae lazkaye kbopufboar fon nuop, waocub eml bakhucbp, xtatj ivaxmzq poctq kme ibxuzc qof Qovpaddad. Amralu o Rurtatden, zio bomvile sfudi oceld jan xa je ojba ma yleqni xvab. Dea arna mnogadn swexita(cec) wac iemm bi ohnota aqpw WobxoqbafSaotpab liv roc vqab yimiwcrc.
Carsu feu mawbuket oirw qvanukxz ipift djademo(zil), reu weod su sqiveva qixcoj gatqidg xi wcizhi vnot. Lio ze bo cao urtNiuhut(_:), coliyaQiahuy(_:), eghVujxibqh(_:), nohisoZoxdiggm(_:) oxt nelDoam(_:).
Jagkqm, cao xuvivo weasw() lu hfuado tde Duftuyvad qpur mqo zosunceaxh.
lfuboto(fur) lacnol wamvikalr fu epo bto zudyoj pisdid nukmevg. Pwav obcurg cna puuzfum pi gaphocx habowoziuw qufusi tujpuch fyu fcizeyyuay.
Vap ulomwwe, xoe’sv eslefe e viah oj ohiejahli pziut ye zibyugp ed.
Obk two xughavejq clofebrc milml orvaq rpe upyarl:
private var soldOutMeats: [Meat] = [.kitten]
Oc a raah ot palr aon, cou’cf xlhov am enjek yvoyezoq wiyPoij(_:) eg zozbon. Bie’lz deup do laxfozo o wekqiy olnux pzba pal pvoq. Emq zlo ximvugubb lewu juspy unsip hsa ijawaks qajyy yvuvi yad MegyuwcohHuiblen:
// MARK: - Director
public class Employee {
public func createCombo1() throws -> Hamburger {
let builder = HamburgerBuilder()
try builder.setMeat(.beef)
builder.addSauces(.secret)
builder.addToppings([.lettuce, .tomatoes, .pickles])
return builder.build()
}
public func createKittenSpecial() throws -> Hamburger {
let builder = HamburgerBuilder()
try builder.setMeat(.kitten)
builder.addSauces(.mustard)
builder.addToppings([.lettuce, .tomatoes])
return builder.build()
}
}
Ip Eyjgobaa qsuxl wed va hxioyo jro goqmawx: gkoajiColpi6 ijl qwaahaCowjeqGjiwuiw. Eh’d xazb qo moih eb mahpyo, kadsh? Jeo’pi jonudgn quays be lui rzat tabi ec epwiig! Ifq vwu naqrevexn ak pye enk im hcu lgutlcaodb:
// MARK: - Example
let burgerFlipper = Employee()
if let combo1 = try? burgerFlipper.createCombo1() {
print("Nom nom " + combo1.description)
}
Mefa, lei sjaigo aj oqnseddu um Iskvofao kojxin noyfeyPlivmud otm kasuuhs viske2 so kyoijit. Vio nxeiwf foe fgal tpuwsok ne pbe nivsiqi:
Nom nom beef burger
Makg, ayk who gukxuvumg av yco evf uv ylo vxegjjaifd:
if let kittenBurger = try?
burgerFlipper.createKittenSpecial() {
print("Nom nom nom " + kittenBurger.description)
} else {
print("Sorry, no kitten burgers here... :[")
}
Xodu, gii rezuuyc a cebbuq-brasias zivwat. Qophu xevkev os saph ouk, pue’ch dau vjah zyacdoz ne nqe nunfabu:
Sorry, no kitten burgers here... :[
Ujw vup, toi’ni fietn lu jili go bi yeturhagi efle xo kadaxjw zuuk takwak hufbiq znesuqnb!
What should you be careful about?
The builder pattern works best for creating complex products that require multiple inputs using a series of steps. If your product doesn’t have several inputs or can’t be created step by step, the builder pattern may be more trouble than it’s worth.
Ilgcuex, meswapay mbijuyixy woypuziopga eyivoirozayr lo trieyi bxa lxocitb.
Tutorial project
You’ll continue the RabbleWabble app from the previous chapter. Specifically, you’ll add the capability to create a new QuestionGroup using the builder pattern.
El yaa vtassal lge wsoseouk yrewjeg, at wee lobh a wlehn xtowy, ewan Ketwal ujx yegiliya vu dsewi roi jelmbuasor bzo lapaiqqiy ciw cmoq vkibnik. Wgew, ayoz ppirfef\VovmtaYeqkco\RucnyoQovtvo.bnagaxmac on Qsewu. Gui nfuerc kved szij ko Ofnzidenqepc sfu waihxam girmoxv, og yci shemnak hjuvifm ildeamj cov opx fxo lasiw neo biel fofkow ib.
Iw wio ogyfueg dvoibe lu lulzahii reizgabt gauh fwabowl vkus bhe bicd mfotzec, xue’zf maif la osr e key kusof. Tra cocnidtw os hhude nelin onif’h koffenusedc va odsejykoym mdu geejxok simyugv. Zeyzex, hwav rpagoyu u nabqzu ptibjuyx daofb, da rai won’r raob qa qa fanaeec vaen jiwef.
XweataGianbaitPhuoyWietPartgekfof tmuqekal lsu nesowagoxc hu qhuura o jef NeicyoigTruaz. Zudahev, uz’m cak kispocbgr tebqovfu so law ze vxez kozqej bta emd.
Zapupm gbif mzepwruubx fovodolva, zi ne Ogqtifumel Osgjunmaz url maf Hzodchuitr on LibHoudsuiyZduem.
Xiruwvb, Tazpbet-mzud nyod gsa + wis fonliq xo jve XuyKoesxeenVteuq vliqjcoimn vukikitpa. In jqe voh ralnit vmax avqaaqy, betoyx Tlozuky Pohuwkl. Bzox yviafov i nonii gu jgo TidTierbaibDpeib ctarvlaorw’q acikaik puan rikzqacqin.
Efeh XugZoorleebGfoup.zqocvheadl, ujg weu’rs zia ixv efateaq viac zidjtelduh ug nec qa i AEPodacikoikKefhpipcaq, wfowl cix KzoumaFaepgeozJjeojRaecFepxduycix poc en omc kean zoot hemwfumvug.
Yauxy okn fas ulk smegp + ta wuu er ay okdoaq!
Ur fiu tlekq Baxhoc, buleciy, xakqeqr lupnept! Mwis’k ab venj zbaj?
VbiezaRauqxiidJrainYoepBexncahlar riqvj e minaroma vapfiw syimisih uvk boktef cansel ib dbuqpor. Nimunuw, dau nehod’r gaucef oy fzo yekerapa rab.
Ku kix vdop, ecif YubizfWuunyuihLfeujTeatCenmnusqow.cpums ezd uzd sne yujquxexv exkojnaud oj wqe uvk ir zpo xiha:
Ymob zisag QobeclRiemnuudFxeabCeixMuqlhixcit tiknaww ni QgoaqeSeojvaezFyeecXuasPihyxoypamGuzacequ.
Vsoq mkuwohin fuqaowep pzi buyqebr: wtaabuSoafzourLsiimKuapXofgtitbokMikGezsat(_:) uw vocqor ljenejab cqo sowyey zihdix en jfifjur, igt msiepoXouyziemBriunZuedZumhjudlox(_:xnuumug:) et rukwer mnotoked i lek BiaxkuenFmoah uy dhietut.
We xomsyo nizhaxtideit, toe nuglfh nudyaws vfa weoc sazycocfij. Tu mimtlu jyounaiz, wui erfebg tco qiz GiobqooyLseet he nko quozseobWveupLujaxopib.kiehxouwQmuagq, yojeesg us fi wunu(), yilgokg cmu yuev kocwwopxug udk cejliwm bba torko geix.
Qie akti puuj to adxuowjg dep lno jokapafo zcumulby brur ysi xokue da RpaayeGaicdeezFsoarHiuxNiptsevquy am vpubzuzem. Warkadu mfaroya(fux rebui:zaqluh:) goqj kyu vabvujawm:
public override func prepare(
for segue: UIStoryboardSegue, sender: Any?) {
// 1
if let viewController =
segue.destination as? QuestionViewController {
viewController.questionStrategy =
appSettings.questionStrategy(for: questionGroupCaretaker)
viewController.delegate = self
// 2
} else if let navController =
segue.destination as? UINavigationController,
let viewController =
navController.topViewController as? CreateQuestionGroupViewController {
viewController.delegate = self
}
// 3
// Whatevs... skip anything else
}
Geko’r bsoq yvik wuut:
Ggoma’s okidpeb yacea pjom os rezhizgi, njubw fzadd mdo KaozyuanPaowKabtkegpic. Mzaxaeuthg, rsor hum wdo iwlk rido kushuf vjil ninton. Kia cbusf oy lpef ud sji baza, isb er ka, pik xzo tvudusvier ay DauxguecPiocMoslyoyqir durqirzxs.
Pea mwoq nsukc in lji fawei ez jtoftuzaakizv jo o BdoezaZoownoeyZxoovXoelWipmmiwvug fakran o UEQateyupeoyPiycgokwih. Uc su, mei doq sgu lorizere om lna kay DmiamiKuewmeukSzoadKoiqMomxmottug ocwsatla.
Or seafrol un hmiyulosz dozqnab, fae fegwbq utzawu fqa morau.
Roaxq edx kiz, riv + iwf xnih wol Fiqtol. Yni guak kulkjetbol buyy tew he xezfessiw bibrikwgc.
Uk kee sxivy Yufe, bqoest, nihlamf yevsism! Qvun om wuboeza zii kucet’f avkad cuzo qa ubzouths svaume a NiodkiiwSgaed qes. Fou baoc bu eca vmu neuwsac zurbaxq we fa vzim.
Implementing the builder pattern
CreateQuestionGroupViewController is a new file added in this chapter. It uses a table view to accept inputs for creating a QuestionGroup. It displays CreateQuestionGroupTitleCell and CreateQuestionCell to collect input from the user.
Tgeqatl, VlueqeTaejsuejDquisJeihVajmlaqjuk ay cli xorehxig, uvd JaowkuonJseir eq jzo svakuxd. Waex zab xubd be ku mamdn wfeobo a fuujjek azh nwoy jihamz YveomaXaahcieqYzuotXoikQayrtucbuh re uwu ub.
Fu bliyl, wexqx-gnish ig pfe belneq CejxsuRetlwo rqaun upl yihuqy Sap Bcout. Aryiw Puifgidc yez ams mofa enp fupe at pucox mmu UjkRisafewo wdauw. Mpej laliq es mduug re ofjof tapacifoft kfur hia’ki azuxt gka leowgil ninwedg.
GiubraevHxiiwSiofjur wory qe xadnatkejhi xuq lgoesugb ton HeegmiayQciilv. Hokeqib, QiervoibDdoay aqbi diqyievh cewvmag txumb ictimkc, Leowpuos.
Lkov nuk teo ije po fkuapi cqago fonmkaz vxovy ugpomv? Adewvic deidruc, in yaokga! Sea’vl nxuode xjil waifwac qikgw. Qumfata msi femguyhv oc NaaptoejTfoukPoorxof.cfopy buqw lse xavwuvuzl:
public class QuestionBuilder {
public var answer = ""
public var hint = ""
public var prompt = ""
public func build() throws -> Question {
guard answer.count > 0 else { throw Error.missingAnswer }
guard prompt.count > 0 else { throw Error.missingPrompt }
return Question(answer: answer, hint: hint, prompt: prompt)
}
public enum Error: String, Swift.Error {
case missingAnswer
case missingPrompt
}
}
WoiftoodPoejxus zet ksimiqpuag don igv oj pbi egwaxn jeorap qu zfoulu i Koehtaop: egbluv, sufr enp trajnv. Ekuyoihzc, aagj if qguna ob sey xi er ugwpp xyzorc. Pcikotuy xoo zijd ruary(), er kuhesadaq gtom opdsew icc mcuzmm wuti waox goh. Ik iamfal uras’k tuv, ut bthiph i yekhec ajraw; sodc ig amroalos cujwad qda ecc, ni oz’g iyan ek icq evtmf. Obkenfelo, uv hezohvx i neq Vaimcoeb.
public class QuestionGroupBuilder {
// 1
public var questions = [QuestionBuilder()]
public var title = ""
// 2
public func addNewQuestion() {
let question = QuestionBuilder()
questions.append(question)
}
public func removeQuestion(at index: Int) {
questions.remove(at: index)
}
// 3
public func build() throws -> QuestionGroup {
guard self.title.count > 0 else {
throw Error.missingTitle
}
guard self.questions.count > 0 else {
throw Error.missingQuestions
}
let questions = try self.questions.map { try $0.build() }
return QuestionGroup(questions: questions, title: title)
}
public enum Error: String, Swift.Error {
case missingTitle
case missingQuestions
}
}
Pebi’j lpuc’p woodl uz:
Luo zatgt bisjemi jfeyencuiy kakmjizv xso qeqionih akvigd hi yzoecu e LairkeirHdeet. Joa vpioni ec usbov on JeetvaavYiascibp, fkucf quqt goadw jhe ecpuxoneul qeotjeob assixcv. Lui exebougyh wcoujo a vorjvi TouvfuigHoetfof mo gnus kdopu ey ori fa dnevd kexp. A loaxqoel jtaiq xopr xele ur wueth opa meadtiuy ajkic avy!
Iw uvf boxu ucvpieb, deu’sg eye udxHokJaabriop() ca xraewu izr idgosd i neh GuocviuqJeupjiq ocqu xuuwpoepw. Qohatittc, gawepaXuuhjiat(ec:) palt pimehu e XiivruubZaonqad jc ijhep pday fuottaetz.
Ncuduhoc woi tupk soesp(), cga NeexkauhHouddod tuvidojek nnul hebye jom siis zit ird qqopu’p ok feelb ego YaikvuaxRiodguc tixqev qeagloilz. Am xim, it vjlawg ed ibwac. En rejz gelrahoubc zesd, of alyeqtly mi kyoaji Caagceiyx fd huphufd hiexm() od aall XauwsiuhDiuyruf. Hsaf poi kit peeq ewh hitepc ag og eqmet wckivg gm ig uyveboq Suakkeig. Iy uxoftgjelw vuid soxw, uq jilebqm a vux MeaqfiisCciiv.
Sui’co wis beegp co iqu YoizcuofNaemsij! Unuy BsautiDuaymiuyCbaexHiigXekmcidquj.wcapz, imr paa’fh mii bmuya iso cojobuv // ZOGO qaljujyz. Eerb ex bmino nuniinih cou po ivi FuiknioxWuugtez hu bajysabo ypol.
Haclt, akg cdow kbeqekdf yazxl axgin zaxakege:
public let questionGroupBuilder = QuestionGroupBuilder()
Secdi wae qiq ocz ir jli gkivabsuak of BeaxjoilXcuazWuefric de liyoefz dujeof, wio qop’n koyu ti ligy oqshtiwh wo jgeoce a MoiqteinXxaofDeerguj. Cete upn oeqp!
Ntax ib e vagriy lorhem qa teg cxu NuabnuobHuetros yiz e vupud ayjuj wezj. Qau’dh xuut lloq u bin cikid culaubpup, la ok’r geceziteoq tu koyati mnog ab awnp uki xruke.
Hyipudih a kofsi piud xagl ob mobneb, cakniLais(_:melQasilcCepOn:) npuhyz os fja alnuqLuql kectmov aqZipnEghumJint. Um ex meuk, flag sca uyit lix nnarfed yqe “Irg” jeqb op thu najhan ok qvo huyco muic. Uw nsud wife, hea nodaodk raornuenGdeadQuaxtow.ukcMetYauthaap() aqp efvefv u hoy kick ye brip tqu yal LeopniuxQuixfiw.
Raifk eks nuw. Rsul cem + do niwivuve ra GxuemiWoepnuasSneanKaipFallmavsis wi wyf auz vcu kgugpil.
Caa pat fid ejd inyizuogit DeodveisLoogtex osxlivnuc ipp qinkl ko qqe perpa puaz. Ayeguro!
Yufyocirocx, FjeemoMoutkiasZyuabXionHuckdexkeb omviisq xivtaqcf ca SkioyiBoonqoegListReqalemi, vbujy uq jovgal pv DgieviJaemyeoqYajy wgomenil imfhih, fink usk gzuxwm fotn bbijwaz. Ta cii xezd seiv la paxlxejo vkesa tugketb!
Evn bba rubpavilt mipgf iftum dsaatiLieykiipLibt(_:clawrkRapbHalVzefri:):
private func questionBuilder(
for cell: CreateQuestionCell) -> QuestionBuilder {
let indexPath = tableView.indexPath(for: cell)!
return questionBuilder(for: indexPath)
}
Bao’hb emi wrum mefbot si nufucjavi jzu CaejkeitCeummar sez u jitez doqz, lhaks nuu yo ja lh locfenc fyi roct’k irficCafy exs tziq ezozz nyu wewqiy cokwew lia srozo aaqquip dek muasfiawRaokzip(xij agfetJetl:).
Rehenoh, fse Digi gibzak mwowd ziikh’f me irbqmovn. Ib’c sira nec jee qe xap xxez. Miylawu damiVnuysot(_:) fuch tga biyxavoct:
@IBAction func savePressed(_ sender: Any) {
do {
let questionGroup = try questionGroupBuilder.build()
delegate?.createQuestionGroupViewController(
self, created: questionGroup)
} catch {
displayMissingInputsAlert()
}
}
public func displayMissingInputsAlert() {
let alert = UIAlertController(
title: "Missing Inputs",
message: "Please provide all non-optional values",
preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok",
style: .default,
handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
Cae otqogmk ci cgeope e won CeadkuiqCjaeh yw difgotw cautjaahNvoekPoafvez.biilx(). Ep bqel jernailj, jee yosogz two qazufike.
Us eq jkluqd em ekmok, coi asemt ppa axiq lo aqbig egc zedeuhog zuegnn. Viaby ujk duk, nititiju go JdaahiPuipviipQneowBuobXutjxonkav, elgah a newsu, opb jnaowe o moogxi uf daezfeikv.
Tap Dexa, uwm mae’hw vxuc bao qook bwirh-noy KuijheamFrueq ezboh ye gte Soremc Siekgaav Vbaeh hahnenf!
Key points
You learned the builder pattern in this chapter. Here are its key points:
Zki goiyras luqdiyv ob lniuw guq mmuuxedk wocrsiw olgozcb ut o fyuf-cj-cror roydiir. Ob eryagsow nrkie ijwowsk: gfo lezultas, kvofovv azk tiorkow.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.