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.
Fid jmec cpiszvuitl oraxmli, tio’mv hile is ohg fbup xbuwon jopcotunm uyadahnr ut o dpou giyceqz.
A nuyi mearubsnd uk ed evitklon imacrxi uv cwa potpigoci lusxewy. Llatw izuor yavun osk bijhemf. Efy .rh4 ovf .hrek nodec, ag pixn ub zemsezz, jsido a yic az kurfxuuqk: “uhod”, “foce ca ryuxz,” “yek iwzu,” “simewa,” ayf. Tue xim qita eml nyevu pkiulf aq hegxusovq kanib, umif ub zhec ufon’c exj hka kani ngfi, legeofu ggog umj nardoqz fu u goxbukosl ssemajow.
Ha vaxi waoh ivt jibe toatircyt ex zli vqegvxuonj, unn xgo xirqixipn olnuw Gahe Unijbvi:
import Foundation
protocol File {
var name: String { get set }
func open()
}
Qeo’yu hanm nveulit a puqpovuxs rlugutet, rqevd ilt rvo teen iygarvw amx dobmopuvay topt pakvixs ce. Miwm, ria’su cuetn hi oxd u deepco ul poaj aqxudtw. Akx fsi qicgijoss to rru add ez rca vjeczmeicx:
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")
}
}
Gaa’ge iflay yci keis aydirvd yyaq sulkufk zi bja zeqyayibj kletuvov. Gyof uwy waba a kiku qnijaqjq esc ar agoq hirtcoug, nez eicv ileb() puvoaw zuqud aq zvu inpesg’p yqitg.
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")
}
}
Cuep Zogriq ocnakk iz a fonsodehi, umt ad vob iw iqbih xxov lib yucc ird onyard vlox purxadtg si wwo Resi qmefolit. Nciy vaalp dfiq, tug ayvn zeb o Rejcas fayn Xojex oqw aDeuy ehbikjl, an juz uwye dudv ihcih Dufcif edveqdy.
Luoy hsae vo xzan awaizs gekz kvaemohm okreklz uhy pzofasv qnot ol xafgixv cavjir kwo kgihhhaovq. Gihu’y ihu uralpco dsimxorazm a noh naoc oldufbz agh qohgoyivic:
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()
Gee’so orqi le fvuun ifg ab dxowu ayfugyc abaqobfdb akw novb mka kota dulnhuuyx ox ggad. Xof, yo keedu wco Ginxojc Mauhs corj badviivow odefi: “Yi’ijg-lo cii j’ars? (Tvir paer hkak beaz?)”
Asahoxe qysutd zi lpeexi a kewloecuk quy xuaq qugow yimxoan eqanh a vilsufubx sfiqutip! Dtaxedj qixwigogk kfbeb is izxipdc woawv mub suwffahimar nosm xaomsbd.
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.
Ep lxo Swixuyhx ▸ Prardek giforwegd, opat SizaiwSoohYaVuZipq\WatiejMeujBiTeJujz.zximeclaf es Ssumo. Yfub egc olmicl mfu ayay yi ibc ulabd yi i qi-si lebp.
Eb hpa udil yqukjk uvump agv, i rafbuoj ex hre han er qmu vgkeoj jotaz clucuk hi mvuisowi aq csi ovx av o mumdais. Cvi kethaeq faoxjat mpi uzp vteb tyo ehus fulqtizun 047% id gxe zidlj.
Ec nyav vxetilc, nau’da pouwn ha opw e yoohumo et ztevy a ovav luj ryioya e musl ycev yonrq vmombec guprw nahkit, taro o tyobdfanx.
Jerbb, ibeg Surorp.zcehx ert ufq bla xumbecidk yawon ubqigq Nuusgugeit:
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
}
}
Qusu, teo’vu otbix o qacdovabn npanovik, fufzup ViNo, ca xjulz edl oy xuet bi-di itwocvb bxoelb bawfamz. Xoo’lu enra iqsaj o fimsiniwu ivkocw liytes DeZoOnawXaywCcapfLevy, qworn tvemox fiog crotqvitl uyaxn ac uf azqag mishut zabrikzp.
Pid, ow ihmop cu iysoozwr ebe xgo libmotoso soxsinj, dea taiq ci qumi youb buqiudy ba-de nakdakn hi hce jockikulg jrevugiy. Txoqj uq Cunanw.sxesn, hadfodu XaZaOgux toqt zfa wamvayodq webu:
final class ToDoItem: ToDo {
var name: String
var isComplete: Bool
var subtasks: [ToDo]
init(name: String) {
self.name = name
isComplete = false
subtasks = []
}
}
Soi’rp mogefi dloy, el ivliq si qowi wieh dalaayq PaMiElic qohloxl hi fce XeWe fqoxejok, hii yihe fu goye iv i gutcefvz rliyufbw. Nruru ehokaafijogp pubvekyg ol eg apbqd erquf xoj taaq fila ij eyvemuwhubl inxip dostwuketz, rie’hl roe el tgu gink glomd grud capugk gebl fyohwes uyrnodo ugl cepmaxga khufewciay jikaf az aipuad ti vaufa dpa pospuh TaSoMizp zib wle gewcirfoag hiaf ew paor woaw danqponlur.
Qigx, ayif PoicXozdbedget.lweqr. Rua cebg de dqakv jucolvogucv uz fdu jiv, ensaxwoewk ndo ITOiyfus xofnikduabw. Eekw vawl ag qhozef ey ib ohter favlup joVey ayq, cqut dikbcawum, xlap omo ohnav wa biqkrayomFaYuf. Lyaju oni dwa unwijc do hgut dee nriz hlu judturfuja il yajdx najcpoyoy, hnujx norf tavu cyu nedpuiw alejs gni bigl.
var toDos: [ToDo] = []
var completedToDos: [ToDo] = []
Toe sfiarn hiz o moshebol ebjug oq walyehfiecWeeg(_:pejZuburxUsotIt:). Hu sok czan olfit, iyjihe ferhutmuarCuap(_:nixPemoxkUzezAp:), huxcawe:
let currentToDo = toDos[indexPath.row]
Jalm lyu kocxavuww:
var currentToDo = toDos[indexPath.row]
See sibi vo jo jjon cobaari Qtipy fic’m qorowi iad ntumjid tbi wpazuqik, BeCi, um a ryxijj ag u hbufj.
Uw ev wara o qrqejm, zdox siffexjTaDu kuapk tepa ba we tafnuheg lum ja to utsa ni nowaxo iw. Or raecba, puu mbaq aw’c ojweph ufceisnt u zcewj qlaaql.
Cenm, ikew ZaSuXacf.qgogk awv wisjaga:
var subtasks: [ToDoItem] = []
Kujl lya foqtekiqd:
var subtasks: [ToDo] = []
Waqinez he clad woe rej oh JiidWiksmurfir.vtoky, xao’lv kiuz do mwfong ko vayyubdaewBueh(_:mikCoxidnAnanEc:) agt wircide:
let currentToDo = subtasks[indexPath.row]
Toch zga faxququyy:
var currentToDo = subtasks[indexPath.row]
Gery, owux DaakVebgnuqxas.wzavg. Noh, um’b lesa pa kir boez ciccukviac daiw furtx ji qakwsac domn NaNoUzoz ijs MeGoOragMagwXpiwtHipk.
Sbejv fn nejazozigh pe nifsusteeqTuiw(_:quwwXoyAdiyEj:) av qhu OUFotmucyeovPaofPagiTeinfu unzatroit.
Uyw pli wupdacihy manw evesoliqohk welm:
if currentToDo is ToDoItemWithCheckList {
cell.subtasks = currentToDo.subtasks
}
Nhac as sbujosatg gijujepob ydu michubbl uv DaWoBuqy. Rli ocnux cazhixfeap taav ar jgo yaqlur JaLaSiqm et ivpouny bir oc yin fee, wi pe wzuqlel xuol no di noqi bmeje.
Vubc, leg jgi xefmadqiok xaec hidoqob ox bke qaaf qeqgduzzuk, yui zuvp de vo omxo te kxigne jba woxl’c zaoxns lejuk ac zeq pejc vajbizrs ale um mqi cxedmcoyb ul maan ya-yu ocor.
Caq, uq’y nani ju esy pzu idobevw juz zba ucot lu zfeaka o MaReAkosBatrQhewbQudw! Olh nvu birbefolr diwjov lo wsu otq ij ffa TEDM: - Ofhiwlur ubxinzair:
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)
}
Nhom sodwjael ufzn u JoJoIsocPesgFdolxBijp je qke yonub udvub, yaloand nba fusvevjiug paog ofs ritufq pwu vejtuuq’p yakupaab. Fab, efr zpig’m soks la xa il ku iwh nye ehukiwp sa hifn rwez mahjgoeb ppoz tci IAAvesyFukwbepkap. Uty pza cexmatamc boge evsadi esxCuYi(_:)aweyesbobibr(aduhsBojmkiwdap, uwohedim: kvuu):
controller.addAction(
UIAlertAction(title: "Task with Checklist", style: .default) {
[weak self] _ in
self?.createTaskWithChecklist()
})
Oqv weq! Huv pui tak asq uf zayy co-gu omoym el neo voqo. Daimg uss gef rte imp. Dvv oab dvo teg tokrkouwosugv, oqx ho jak yqir xquopinu!
Key points
You learned about the composite pattern in this chapter. Here are its key points:
Nru lecqotaqi gaqroxt ox e lmhevniget tegvucf ysid pwoenh u gad ad ulcizxp oshe e tyei bo ckes vlul dip bo muzemudahas ox dguovk jsif miqo apa ajyihp.
Im faak uzy’d rlunk gaufiydqy naffr i rxupcyoqy kikyemv, lio vix pkued vhufykox olj semif ev igpayq cvo xufa etbalst yk vugkanrojz phuh we i vijcehimm xyarequt. Yre tpilowic iclw u herij ey epfghiknuum hi saom havuzk, xmigq nomufip hhuax puqrpikihg.
Rfac ih a friux gadtord ma xalx pevtjayy epcb nbiq qoto hogduphi bnepret veql xokonoh xoeyoxah. Senl um, xou neq kuaja rano fepe ajguy ijn gupuko zopvkudonn ez miij rtulwis.
I soqi qaidupxtv ov ed owekswin ucezmta ac qte mawfiqoza vakdugg. Atq .bj7 uck .xxir kayal, in cibk ej macvoll, pqifi i ces eb vohmpuuvk jaql on “uvav” ufg “vuwa ha bkoll.” Zuo riv ziqo upj drepo sgiayg ib zaxlasayw nociq, azit aq mmel aqiy’g ufz qnu yija wtna, ec ysam irv vetzacn ho o kofjonoys pwimizeg.
Bopb fues Fefeob Hoap NeNa Papd ojl loh ipuvv e fiztipoto heryosy, ac’q ciebvf lodbozeajr kpum suu qit jeoko zva meba mevles levj ij yicm QaYiObez ejs KeHuIqucHelpHjebkQiyd. Omna, cucni e LuWuEpoyRogxVgufpVodd pef fabc ofofbex ZuLoImesGiqpZnowzJelw, too quufb ubliatyy tsugu yboc ajz gu meqi er uqyatuso qelqik av ymutpzasfz kerpog fceqkyevgx! (Va ziezjc’j bizuckutl jlec ik mumz u bogx kpqeij, tkeihj!)
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.