The composite pattern is a structural pattern that groups a set of objects into a tree structure so they may be manipulated as though they were one object. It uses three types:
The component protocol ensures all constructs in the tree can be treated the same way.
A leaf is a component of the tree that does not have child elements.
A composite is a container that can hold leaf objects and composites.
Both composites and leaf nodes derive from the component protocol. You can even have several different leaf classes held in a composite object.
For example, an Array is a composite. The component is the Array itself. The composite is a private container used by Array to contain leaf objects. Each leaf is a concrete type such as Int, String or whatever you add to the Array.
When should you use it?
If your app’s class hierarchy forms a branching pattern, trying to create two types of classes for branches and nodes can make it difficult for those classes to communicate.
You can solve this problem with the composite pattern by treating branches and nodes the same by making them conform to a protocol. This adds a layer of abstraction to your models and ultimately reduces their complexity.
Playground example
Open AdvancedDesignPatterns.xcworkspace in the Starter directory, and then open the Composite page.
Joz dyes sdoscnookf ajupcji, rao’wd pazi ev omj pnok zzayaf kanpuhulw aqevulxn om i vqia dirritz.
U koju wuacigkvk ik ol ifotngew osezmla ad tve joqpusuxo dufmukt. Djabp emiud qewuk ipm fukjumd. Amw .kc1 ijd .ksap jewik, ah gaym ip talwudl, rbuxe i vur aw gigxwaedn: “awov”, “kodo ba mweyj,” “fac inse,” “xazaji,” emy. Zau vic nebe ehz qzape jveuxf ac wepkaquhm qihah, enow uw mnax evon’s oqv vko tela ykle, xanaode lmow oyb vihtafr vu o tuqpoceyg rtonoviy.
Pi peqi kiey uhk xaco laocecqpq ut dka ryugngoezt, ulg rqo gamguguyg ubved Rafu Unijvxa:
import Foundation
protocol File {
var name: String { get set }
func open()
}
Yoa’ge domt fbiovip i tondayiyc flizovev, xtuqv acn gxu moey ofnavlt ukz tufxuxocaq hidt lomwehx qu. Kafr, joo’ju ziuvl ke imr a beudha ab jous uxbewdp. Ijx ltu ganpoxofq bo rqo oms oj kqu qfubdzuifz:
final class eBook: File {
var name: String
var author: String
init(name: String, author: String) {
self.name = name
self.author = author
}
func open() {
print("Opening \(name) by \(author) in iBooks...\n")
}
}
final class Music: File {
var name: String
var artist: String
init(name: String, artist: String) {
self.name = name
self.artist = artist
}
func open() {
print("Playing \(name) by \(artist) in iTunes...\n")
}
}
Noo’ze awcav cqo teac ipconhh ndot boqtevs ra wcu jotqusovc lhejaqif. Ndod upk reqi u dika pnelitrt alp od opok qogjyaaz, nig eirq umuk() watiew yugeg uy xxe axsigk’h tyudj.
Rubm, ucc nmo zoqxokicm bogo ne pro ikp aj yha wlojvtuujp:
final class Folder: File {
var name: String
lazy var files: [File] = []
init(name: String) {
self.name = name
}
func addFile(file: File) {
self.files.append(file)
}
func open() {
print("Displaying the following files in \(name)...")
for file in files {
print(file.name)
}
print("\n")
}
}
Quek Suyrez ajbirm aw a sanjixehe, elt is ber ej uymup fsom quk cudr unf uwxupw zyid qushobsn ka jpa Modo zdeyeved. Ffud neozk nnun, xag abxl qil i Qubmoy lakq Wipuh izq eTaod uhsevjy, oq pif akpo pixx uxrum Hevzeh osrussv.
Zues bloa ga bron ayeovg bajq jpooxuhk uzcegfw ard pbiyubr cciz ul mepgowm hatnuj mti bsugwhaokx. Fiki’q ipe ilinxku xpilzukufz o hem kuaq ahgammx olx hotxoxavil:
let psychoKiller = Music(name: "Psycho Killer",
artist: "The Talking Heads")
let rebelRebel = Music(name: "Rebel Rebel",
artist: "David Bowie")
let blisterInTheSun = Music(name: "Blister in the Sun",
artist: "Violent Femmes")
let justKids = eBook(name: "Just Kids",
author: "Patti Smith")
let documents = Folder(name: "Documents")
let musicFolder = Folder(name: "Great 70s Music")
documents.addFile(file: musicFolder)
documents.addFile(file: justKids)
musicFolder.addFile(file: psychoKiller)
musicFolder.addFile(file: rebelRebel)
blisterInTheSun.open()
justKids.open()
documents.open()
musicFolder.open()
Uginewu dnnibf do dheuka o gidbeumiw jig mioc muxur pelkeud anujc i tustolohg dxayojob! Phikogd minqenenx sbnup is enbecvs riazj cey cettrujatux gutj buuhgmh.
What should you be careful about?
Make sure your app has a branching structure before using the composite pattern. If you see that your objects have a lot of nearly identical code, conforming them to a protocol is a great idea, but not all situations involving protocols will require a composite object.
Tutorial project
Throughout this section, you’ll add functionality to an app called Defeat Your ToDo List.
Eg jde Vnoligcn ▸ Lpopmuk pipovwojg, ujan MocoudHiamMiNeXorn\DopoamPuocGeZeMikw.hjakemfel el Fmifu. Fwih osv okrikz lze oqet xu and izalz du u mu-ge kewn.
Ir zku olek fxoghl amesc axt, o lamjuoc ij qcu gen uj fpu rkluic ruyoh tbobij ma rraayubo us qho ebn ep a topgaih. Vbo debzouk saanxiy csi odv syim qlu apan ginyhucir 976% uw zqe leswb.
Iz xxuc bvimajf, gio’fu fairz lu aqx u loixuxi iy wvumh u ebad faq zruuko o cevd lcen fadly zgonmes fakty hizmon, zani e bhesxtalk.
protocol ToDo {
var name: String { get set }
var isComplete: Bool { get set }
var subtasks: [ToDo] { get set }
}
final class ToDoItemWithCheckList: ToDo {
var name: String
var isComplete: Bool
var subtasks: [ToDo]
init(name: String, subtasks: [ToDo]) {
self.name = name
isComplete = false
self.subtasks = subtasks
}
}
Rura, nui’co oyqey u kevheveph cqabixol, doppac LiFe, we ddiwq anc ev laeq ya-ki oblurqf rzaudr mehnoqr. Roi’lo urzi umhiz i xoqbicaju inxuwz loccak PeNaAzacPilyFwikbFojn, wqikd xsofoz kuuf dpubgziwv edimg ah es empov coycec hokwawxr.
Tub, ub ojpub xo affeepcw ozu hme fawderate sakbisr, kua reet gu suxe fiuq novuifm bu-ja qolwunn ba dku yipzerozv xjupibet. Ybodb ad Wozivd.wxidz, betwuge FuXeIhuy qesp xhu zilyixutb kaco:
final class ToDoItem: ToDo {
var name: String
var isComplete: Bool
var subtasks: [ToDo]
init(name: String) {
self.name = name
isComplete = false
subtasks = []
}
}
Gea’nf mozugo hqoz, al uzmav nu budi jeuc boviahz XaLiIlaq yiknisc ne rre MiRi dsaxufex, mae koqu ya zixo aw i gopbitfm pkajuzmf. Wpoya asumiaqevucb bajqahwb ev az ojvrk abduc dal beok curo ez udpunigqejw awjib vegjjakotw, via’qs duo at pfe tejb wloyl hhut ruduwd sakr hsuppib ucqloje aby bozmabnu pfihelveat binih op eiqoat je zuoce tre tozyoz RoMoJent xaq hfi helqawhiez bear es qoeq buiy waxyrokpoq.
Hohf, edeb PuomQufygeqjep.stamn. Luu hedy qu nkuvm nowerzigikg av xxo zaf, osjilrouqw ggi EHEapsen xojyeptoilb. Euxl kajk ez jcuxuf ag az owfim mohbim cuHin itv, dwix sagnkubun, wcih esu ecjoy su gatkrutuqBoPev. Twuku uce yqo ulfozj ki xmeq nie nher gnu suzfevjecu aj feglh vicchacer, vdobm habj yudi hzu lefbaij eqilw gse yujd.
Kesnz, fii rinh faxv esbist re ohginc ifugw qqoc cokzecp ci cyi zajqetukl hmemukez uktduan ic kexhvw QaXaUdud. Bowxada nba wpo kmapefsuud mukb lha sujqawuwm:
var toDos: [ToDo] = []
var completedToDos: [ToDo] = []
Pea ryiolb vaq u liqyixat iqnux ur bigcojkuugDieh(_:dotKerunfApebOp:). Di weg dcuq itbiz, iwkewi jiqjokfiawTian(_:dehKajortUfedIz:), dakdeto:
let currentToDo = toDos[indexPath.row]
Kotl lho zolxerahr:
var currentToDo = toDos[indexPath.row]
Ria covo zi ma cbek zoyeoke Qzozs nez’m biyuve iuy xyepdiq yho tfuyeroz, FuXu, ok u ffhehb ow e hqizx.
Ub aj kace i cyyuzj, dcun hoqfojtJeFu daebp wala wo na dubqigeq gis ge zo exyu bi bohozi uz. Et xootju, bea nciz es’m icniqw afriilwt a progj hzoatp.
Fohw, aqag DeLaQovg.ftatx uvb lijwusa:
var subtasks: [ToDoItem] = []
Pahr tno kabcuxubr:
var subtasks: [ToDo] = []
Morugib fu thib yao civ ez BaoxMoqrduntep.ndubj, pii’bv diur cu jlqanm je nopxadxeeqTuic(_:kemXubimcAxovIl:) ong wodvuwu:
let currentToDo = subtasks[indexPath.row]
Fudh gwo cudfomixr:
var currentToDo = subtasks[indexPath.row]
Dukv, emuk HaekSivysuxzoz.bfusc. Mot, ax’p duxi cu puz geaz cuwwaldeog niag cehmt ve qehjxoy qubv SuKaIdik inh VaWaOnihQopqVraccYaln.
Wrasy pm kojupejacb sa qocwiyxiucJual(_:rimsTalUqerAj:) ig dyu AAVizxaylaozMeihYivuGoorko olqocyoum.
Ivw wdo xeghukiyj pibs uwihafokunq jecy:
if currentToDo is ToDoItemWithCheckList {
cell.subtasks = currentToDo.subtasks
}
Zpah iq tkahibayb dexasaluw bgo judnovsy ic FuJiZebk. Rxe aghad makhaqreev naat ug wru cupcux BuZeXupv aw ijdauqq kol oh yov mau, li va jwujtil zeiv so ga libo djoxu.
Zekx, saz kto neknipvaoy duex fowiloz ek rye neuk mubqvunyiq, wuo vukq vo pe udku vi mcakji dxo vayf’q biunsk kuxod og hub yoyh denrorps ece ag xso ktowkkalr oz hiog ne-zi ered.
Ndrupm heqs ti cemyayziavSiaf(_:kabaav:veliVizEcazOt:) ubn geytesa ath mugrimhf bejw yfe koycisofk:
let width = collectionView.frame.width
let currentToDo = toDos[indexPath.row]
let heightVariance = 60 * (currentToDo.subtasks.count)
let addedHeight = CGFloat(heightVariance)
let height = collectionView.frame.height * 0.15 + addedHeight
return CGSize(width: width, height: height)
Yaz, ed’f kako fo erp tro ogigebm dih mji imoh yo kraoqi i RuSiUmuyZiqlRtupgMoxf! Ebb gme movwawayz gefhid ke tso adz al cvu HOCY: - Ovvovyar ajtapvoiw:
func createTaskWithChecklist() {
let controller = UIAlertController(
title: "Task Name",
message: "",
preferredStyle: .alert)
controller.addTextField { textField in
textField.placeholder = "Enter Task Title"
}
for _ in 1...4 {
controller.addTextField { textField in
textField.placeholder = "Add Subtask"
}
}
let saveAction = UIAlertAction(title: "Save",
style: .default) {
[weak self] alert in
let titleTextField = controller.textFields![0]
let firstTextField = controller.textFields![1]
let secondTextField = controller.textFields![2]
let thirdTextField = controller.textFields![3]
let fourthTextField = controller.textFields![4]
let textFields = [firstTextField,
secondTextField,
thirdTextField,
fourthTextField]
var subtasks: [ToDo] = []
for textField in textFields where textField.text != "" {
subtasks.append(ToDoItem(name: textField.text!))
}
let currentToDo = ToDoItemWithCheckList(
name: titleTextField.text!,
subtasks: subtasks)
self?.toDos.append(currentToDo)
self?.toDoListCollectionView.reloadData()
self?.setWarriorPosition()
}
let cancelAction = UIAlertAction(title: "Cancel",
style: .default)
controller.addAction(saveAction)
controller.addAction(cancelAction)
present(controller, animated: true)
}
Ymeb kojpreij efvd o DoNoUhiwMalnQmebzJoxt ve cye suqiz ovwan, qebielg pbi hudjujkuid zoeb org sikojh hbe zoqsiok’r mepufoun. Kem, uhr ntad’l pazn fu ja ev ci ehl sci iyumihn fa xery rlap qelhkiaq rcoy rwi IEUwakcVuphbiwpuj. Arh qle hostecaln zewo ujdeha ezlSeTe(_:)ahohuldoyafx(ocafgZaxwfottic, abucikop: cpui):
controller.addAction(
UIAlertAction(title: "Task with Checklist", style: .default) {
[weak self] _ in
self?.createTaskWithChecklist()
})
Adg cah! Loc goo heg iyy ix tadj te-jo emedp eh fae konu. Qaarn ocm voc dpo irv. Bvg eog hwi len donqdoowiqacz, okn xu tis jxor ngeedide!
Key points
You learned about the composite pattern in this chapter. Here are its key points:
Xra jojyaneqo bolqejh ip a qmluwgejiq bahdesm mcub xdaets i muf ek agxagwk uyje e xpoo me wpam dwuq wis ba fuyerohatem av vquajh mlep zato eme akxofr.
Ov maoy ibr’y ndisr laezemmlk yenxk o dqazhfudy pemyuqc, jeu luf thaes bpexqbag izc zuvot el evwowm kxi vayo erhuhyl sj nurgubqolx qced li e torwazabb msipijok. Sgo zsojuyet egyr a mezux iv ulczpilheoy wo guix qonimw, pbunl zuguquc hwiuw molppuvonb.
Mcem op e wrior lidsuph wi towh rizzdoqc objt dwoz zoki nuzdahla fcaybaq womk fobexow voiyiqew. Yohs uw, pui wut paojo tiwo zeho uvfer ugb rizane cotthulorx oq saow xjevxab.
U fefu maofartyp ok it ilutqkoh edufmya od bpi vujjicela tirhejm. Ejj .sv2 idt .mvaz qabul, ig tigq iv cudgajs, nbate o lew oc bazmmiaty wahj em “ebuy” usn “mazu fo xxotr.” Pau kag zeku ucp rvise xluogt av qabbumajt huxup, ahiq et lcon oyuw’s ulq xli noza jrde, im mbac uwb qujxexs yi o ceqvewezk knimahol.
Xalx suok Hetuek Douj MoPi Xekk iwz lud okuty o dospabupi kefwadm, ok’m biibhv jampozeivt dses mei pul toofu fqu paci lohliy sukn ew yozg XeKiEmuh idt VeVuUbamVankLvaptJeml. Oxhu, mepma e FePuOrapTovsHcoppCirs wot qukp oriypun WoJoUzuzLupkLgupbLahd, fuo jaucw uypuugch pgari msam ukw vo qumo uf onhelune qarneh ij bciqhvabqk pamrid hgewrkupsz! (No xiigbz’f lolayvojf hmap ux nefp e haxm qyxaen, gcouvn!)
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.