Swift Solutions: Abstract Factory

In my previous post we covered the Factory Method pattern, where we learned to select and create an object from a group of related objects. It is only natural to now cover the king of all creational patterns: The Abstract Factory!

As you might have guessed, the Abstract Factory is very similar to the Factory Method pattern. Its primary difference lies in its returning multiple objects; one from each family of objects (as opposed to returning a single object from a single set of related objects).

Use Cases

Here is what to look for in order to determine if the pattern applies:

There exists multiple categories of related objects

A calling component needs an object from each of these categories

The theory can always be a bit fuzzy. Lets just jump into some code and make things concrete!

Wardrobe Example

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

36

37

38

39

40

41

42

43

44

structWardrobe{

letshirt:Shirt

letleggings:Leggings

letshoes:Shoes

}

// Base Shirt Class

classShirt{

// Shirt properties

}

classTShirt: Shirt{

// TShirt shirt properties

}

classButtonDownShirt: Shirt{

// Button down shirt properties

}

// Base Leggings Class

classLeggings{

// Leggings properties

}

classJeans: Leggings{

// Jeans properties

}

classDressPants: Leggings{

// Dress pants properties

}

// Base Shoes Class

classShoes{

// Shoe properties

}

classSneakers: Shoes{

// Sneaker properties

}

classDressShoes: Shoes{

// Dress shoe properties

}

Say we wanted to create a Wardrobe in our app. A wardrobe consists of a shirt, leggings, and shoes. Problem is that we have multiple variants of each of these types, and we need one of each to construct our wardrobe. The Abstract Factory helps by returning a single object from each category: a specific shirt, a type of legging, and a pair of shoes.

Note the contrast from the Factory Method pattern, which returned a single object from a set of related objects. The Abstract Factory returns a group of objects—one from each family of objects!

Before Abstract Factory

Let’s see what wardrobe creation would look like without the abstract factory:

The code is tightly coupled! Our caller knows too much about what a casual wardrobe looks like. What if in the future we want to return a polo shirt as the casual shirt? Our caller should expect a shirt, not a specific/concrete type of a shirt. More on this in the Decoupling section shortly.

Implementing The Abstract Factory

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

// Wardrobe Types

enumWardrobeType{

casecasual

caseformal

}

// 1 Abstract Factory

classWardrobeFactory{

// 2

funcmakeShirt()-&gt;Shirt?{

returnnil

}

funcmakeLeggings()-&gt;Leggings?{

returnnil

}

funcmakeShoes()-&gt;Shoes?{

returnnil

}

// 3

staticfuncmakeWardrobeFactory(type:WardrobeType)-&gt;WardrobeFactory{

switchtype{

case.casual:

returnCasualWardrobeFactory()

case.formal:

returnFormalWardrobeFactory()

}

}

}

Here we do the following:

Create an AbstractWardrobeFactory. “Abstract” means it should be subclassed in order for it to be fully functional.

Declare functions that are expected to return each article of clothing. These functions return nil because we want child factories to override and implement its functionality.

We declare a function that returns concrete factories. The function is static to prevent it from being overriden by child factories. We use the WardrobeType enum to determine which factory subclass is suitable to produce the correct wardrobe.

Now lets create concrete factories.

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

// Concrete Factories

fileprivate classCasualWardrobeFactory: WardrobeFactory{

overridefuncmakeShirt()-&gt;Shirt?{

returnTShirt()

}

overridefuncmakeLeggings()-&gt;Leggings?{

returnJeans()

}

overridefuncmakeShoes()-&gt;Shoes?{

returnSneakers()

}

}

fileprivate classFormalWardrobeFactory: WardrobeFactory{

overridefuncmakeShirt()-&gt;Shirt?{

returnButtonDownShirt()

}

overridefuncmakeLeggings()-&gt;Leggings?{

returnDressPants()

}

overridefuncmakeShoes()-&gt;Shoes?{

returnDressShoes()

}

}

Notice how all our factories are fileprivate? That is because we want to prevent outside objects from using these factories directly (more on this in the Decoupling section). Let’s take our Abstract Factory for a spin!

There you have it! We were able to create a wardrobe through our Abstract Factory.

Now let’s do some quick clean-up: I promised earlier I would expand on why our concrete factories were fileprivate and why avoiding the use of the abstract factory is problematic.

Decoupling

When programming, we generally want to reduce the reliance an object has on other objects. Before implementing our pattern, our calling component directly instantiated all objects needed for a Wardrobe. This means that if we ever want to update what a casual wardrobe is made up of, we would have to remember to modify all objects that created our older version of a casual wardrobe. The calling component had too specific a knowledge of what a casual wardrobe looked like.

We reduce this dependency by hiding details from the calling component. Instead of creating TShirt, Jeans, and Sneakers, we give our caller a general Shirt, Leggings, and Shoes. This gives us the flexibility to return any subclass of these types in the future. For example, let’s say we change the casual shirt to be a VNeckShirt. Our calling component does not need to be updated since all it expects is a Shirt. We need only update what our concrete factory returns to callers!

You probably already guessed it, but we make our concrete factories fileprivate for a similar reason. We hide these concrete factories and only expose callers to our Abstract Factory. This allows us to freely modify each concrete factory since no caller uses it directly. In more specific terms, all our caller needs to know about is the WardrobeFactory, not which specific factory gives it the wardrobe it needs.

Conclusion

Another tool has been added to your kit! Now any time you need to create a group of related objects, you know exactly which pattern can help.

I plan on continuing this series by covering one more creational pattern (The Singleton!) before moving on to structural design patterns. If there are any patterns you would like to see in Swift, feel free to send a tweet my way and I will see what I can do!