Swift Solutions: Composite Pattern

Swift Solutions is a series of articles covering design patterns in Swift. In each chapter we discuss what the pattern is, when it applies, and how to implement it.

The Composite Pattern brings consistent behavior to tree data structures. Though it has a niche application, it is very helpful when applicable. In this edition of Swift Solutions, you will learn what the pattern is, how to implement it, and identify when it should be applied.

Definition

The Composite Pattern involves a tree hierarchy of collections and individual objects. It allows for clients to treat each hierarchical element in the same way.

The pattern involves the usage of a tree hierarchy. Each element in the tree is either an individual or collection object. Collection objects contain an array of elements, which can be a mix of individual and collection objects.

Every object in the tree shares the same interface, therefore clients can treat them as the same type.

Illustration

The Composite Pattern involves a tree hierarchy of collections and individual objects.

In the above illustration, we have a directory of folders and mp3s. A folder simply contains an array of files. Each folder’s collection can contain a mix of mp3s and additional folders. This illustration nicely corresponds to each part of our definition:

Hierarchy -> Directory

Collection Object -> Folder

Individual Object -> mp3 File

It allows for clients to treat each hierarchical element in the same way.

All files in the hierarchy share a common interface, regardless of whether it is a folder or mp3. For example, both mp3s and folders are files which can be renamed, moved, and duplicated. So it is reasonable to expect conformance to a protocol that requires the implementation of file-like behaviors.

The important takeaway is that clients can treat groups and individual parts uniformly. There is no need to constantly type-check every file before using it. This is directly the result of having elements derive from the same protocol or base class.

UML

Component: Abstractions that provide a common interface for collections and individual objects. All elements in the tree must be derived from a component protocol or base class.

Primitive: Previously referred to as an “individual object.” Primitives are simply components in the tree that do not contain child components.

Composite: Previously referred to as a “collection object.” Composites are objects that hold an array of components. While Composite and Primitive objects share the same interface, composites contain additional methods to manage its children.

Just a heads up: there are several ways of referring to our concrete components:

I personally prefer the terms “composite” and “primitive.” With that out of the way, let’s jump into code!

Implementation

In our example, we will distribute a bonus payment to a company’s departments, which will eventually make its way down to the employees. In this scenario, departments and employees function as our composites and primitives respectively.

Component

Swift

1

2

3

4

5

protocolPayee{

varname: String{get}

varbonusAmount: Double{get}

funcreceive(bonus:Double)

}

First, we create a Payee protocol for our composite and primitive objects to conform to. This will be the means by which we treat them uniformly.

Employee

Swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

classEmployee: Payee{

// 1

private var_name:String

private var_bonusAmount:Double=0

varname: String{return_name}

varbonusAmount: Double{return_bonusAmount}

init(name:String){

self._name=name

}

// 2

funcreceive(bonus:Double){

_bonusAmount+=bonus

}

}

Let’s look at our code in detail:

We create an employee that conforms to Payee, implementing the properties name and bonusAmount.

receive(bonus:) is setup as the only way to manipulate each employee’s bonus amount.

Department

Now let’s implement Department:

Swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

classDepartment: Payee{

private var_name:String

// 1

private var_bonusAmount: Double{

get{

varbonus=0.0

forsubunitinsubunits{

bonus+=subunit.bonusAmount

}

returnbonus

}

set{

letsplitCount=Double(subunits.count)

letsplitBonus=newValue/splitCount

forsubunitinsubunits{

subunit.receive(bonus:splitBonus)

}

}

}

// 2

private letsubunits:[Payee]

varname: String{return_name}

varbonusAmount: Double{return_bonusAmount}

init(name:String,subunits:[Payee]=[]){

self._name=name

self.subunits=subunits

}

funcreceive(bonus:Double){

_bonusAmount+=bonus

}

}

In detail, here is what we accomplished in creating the department class:

Create a private property _bonusAmount. Its getter calculates the bonus by accessing subunits—a collection of sub-departments and employees—and returning the sum of their bonus amounts. Additionally, we include a setter that divides any assigned bonus and distributes them to each child component.

Department acts as our composite by having a collection of payees.

Note that the department (and its sub-departments) don’t strictly hold any bonuses; they always distribute the bonus until it reaches an employee.

It also bears repeating that composites go beyond conforming to a shared protocol. They also implement methods for child components management. Having methods like add(Payee:) and remove(Payee:) are to be expected, but beyond the scope of this example. We simply set the child components during initialization, and prevent further modification by making subunits a private constant.

Give the marketing department a bonus of $1000. This is automatically distributed to its sub-departments and employees. Sub-departments further divide and trickle the bonus down until it reaches an employee.

Use Case

As usual, it is important to recognize when the pattern applies. Look out for the following indicators upon consideration:

There exists a tree hierarchy of collections and individual objects.

When it is desirable to treat objects in the tree uniformly, as opposed to querying each component’s type before performing an action.

When the composite and primitive share similar functionality.

Anytime a resource needs to be distributed throughout a tree hierarchy.

Conclusion

You did it! You can now deal with tree data structures efficiently.

Each design pattern is a powerful tool in your programming toolbox. It is a great achievement to always expand on the tools available to you. Congratulations, and keep on learning until next time!