Swift Solutions: Factory Method Pattern

The Factory Method is one of the more useful creational patterns out there. It typically involves a “Factory” object that produces a variety of “products.” Product objects are all related—either through sharing a parent class, or conforming to the same protocol. The Factory object contains all the logic that allows it to instantiate the correct product to return to the caller.

Use Cases

As usual, we point out a few signs that indicate when the pattern should apply.

There exists several, similar objects: they share a parent class or conform to the same protocol

A decision is to be made about which of these objects to instantiate

Enough with theory, let’s jump into something more concrete!

Implementation

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

// Parent Class

classToy{

funcplay(){}

}

// Child Classes (Products)

classTrain: Toy{

overridefuncplay(){

print("Choo Choo!")

}

}

classTrampoline: Toy{

overridefuncplay(){

print("Jump High")

}

}

classBall: Toy{

overridefuncplay(){

print("Bounce")

}

}

classNintendoSwitch: Toy{

overridefuncplay(){

print("Save Hyrule!")

}

}

Here we have several toys that share a similar interface through inheritance. Fairly simple stuff. Now we need a way to determine which subclass of Toy to create. What we need is some custom logic. Enter our Factory class!

Swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

classCanadianToyFactory{

// Implement Logic

staticfuncproduceToy(forage:Int)-&gt;Toy{

switchage{

case0...6:

returnTrain()

case7...9:

returnTrampoline()

case10...18:

returnBall()

case_whereage&gt;18:

returnNintendoSwitch()

default:

returnBall()

}

}

}

// Using the toy factory

lettoy=CanadianToyFactory.produceToy(for:20)

toy.play()

We have a Canadian Toy Factory! The Factory provides us with a method that takes age as a parameter. It then applies custom logic to it in order to determine which toy to return.

Benefits

The CanadianToyFactory allows us to consolidate all our creational logic into one place. Imagine if several classes had to reimplement the logic above just to create the right toy. Our code would be a lot more repetitive, which would violate the design principle called DRY — “Don’t Repeat Yourself.”

Why avoid repeating yourself? When code is repeated, future updates to it would need to be applied multiple times (one for each time the code is repeated!). As you can imagine, that is fairly bug-prone. In the future, if we ever need to change the selection criteria, we only need to update the code in one place.

It’s also worth noting that our caller isn’t just ignorant of how the toy was selected, it also has no idea which toy was selected. By having them all share a common parent class (or in other cases, conformance to a protocol), we have allowed our caller to treat any object returned from the factory the same way: namely, as a Toy.

Supercharging the Factory!!

We could finish here, but we can gain a lot more flexibility by tweaking our factory just a little bit more.

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

// 1

protocolToyProducing{

staticfuncproduceToy(forage:Int)-&gt;Toy

}

// 2

classFrenchToyFactory: ToyProducing{

// Implement Unique Logic

staticfuncproduceToy(forage:Int)-&gt;Toy{

switchage{

case0...7:

returnTrain()

case8...15:

returnBall()

case_whereage&gt;16:

returnNintendoSwitch()

default:

returnBall()

}

}

}

// 3

classCanadianToyFactory: ToyProducing{

// Same class implementation as before

We have done the following:

Created a new ToyProducing protocol

Created a FrenchToyFactory that conforms to ToyProducing

Conformed our existing CanadianToyFactory to the new protocol

The big benefit to having a ToyProducing protocol is that it enables the creation of multiple Factories with distinct toy-creating criteria. For example, FrenchToyFactory has unique logic for toy selection: It removes the possibility of returning a Trampoline (trampolines are not popular in France), and returns certain toys at lower ages. The demographics and the appeal of toys are entirely made up, but you get the point. You can now have different ToyProducing objects that create toys differently!

Conclusion

The Factory Method is a great pattern to apply when you have many similar objects to choose from. It keeps code clean through the consolidation of all creational logic. We also gained greater flexibility by allowing for the existence multiple factories with unique toy-selection criteria. Going forward, if you ever have your hands full with several related objects and need a way to instantiate them in an organized way, reach for this design pattern!

It is natural that in my next post I cover the Abstract Factory pattern. Hope you all are looking forward to it! 🙂