Mastering Swift's Development Blog

Follow Us On Twitter
  • Jon Hoffman

Factory Method Pattern: Protocol Oriented Design Pattern

Updated: Aug 22


The factory method pattern is a creational design pattern where factory methods are provided to create instances of related types. The factory method encapsulates the creation of these related types in a single location and will return a new instance of these types based on certain requirements.


Problem We Are Trying To Solve

We have an application where we have several types that adopt a specific protocol. We need to create instances of these types that are dependent on certain runtime requirements. Without this pattern we would have the logic on which types to instantiate embedded within our code, which would create a code base that is hard to troubleshoot and even harder to update.


Our Solution

The factory method pattern suggests that we create a factory method that will encapsulate the selection and creation of these types. This will decouple the selection and creation logic of the types from the code that uses it and encapsulates this logic in one location making it easier to manage.


The Example

In this example, we are building a drawing application which has several predefined shapes that the user can select. The shape types contain the logic on how to draw the specified shape.



As usual in our design pattern tutorials, we are going to be showing the minimal amount of code necessary to demonstrate this pattern because we want the focus of this post to be on how the factory method design pattern works and not on implementing the example.


We will begin, as with most of our design patterns, with the protocol.

protocol Shape {
    func draw()
}

The Shape protocol will be adopted by all the predefined shapes as we will use the interface provided by the protocol to interact with instances of these types. Now let’s create three shape types that adopt the Shapeprotocol.

struct Square: Shape {
    func draw() {}
}
struct Triangle: Shape {
    func draw() {}
}
struct Circle: Shape {
    func draw() {}
}

Each of these types adopt the Shape protocol by implementing the draw() function. Finally we will create an enum that will contain a list of the predefined shapes. This enum will be used to specify which shape the user wishes to draw.

enum ShapeType {
    case square
    case circle
    case triangle
}

Now that we have all our shapes created, we could very easily create instances of the shapes anywhere in our code where we need them however this embeds the logic for the creations of these types throughout our code base which can make troubleshooting issues very hard. Even worse, if we need to change the creation logic or add additional shapes, we would have a tough time ensuring we make all the necessary changes.


A better solution would be to create a single method that encapsulates the creation logic for us. The following code shows how we could possibly do this for our shape types.

struct ShapeFactory {
    static func getShape(_ shapeType: ShapeType) -> Shape? {
        switch shapeType {
        case .square:
            return Square()
        case .circle:
            return Circle()
        case .triangle:
            return Triangle()
        default:
            return nil
        }
    }
}

The getShape() function within the ShapeFactory type, can now be used to create any shape that we need. We generally like to make the functions static, when possible, to make the creation of the types within our code very easy. For example we could create an instance of the Circle type like this.

var circle = ShapeFactory.getShape(.circle)

This enables us to have a single line of code to create an instance of the type we want.


It is a good rule of thumb that if we have several related types, like our shape types, we should think about encapsulating the creation of these types in a factory method. This will enable us to very easily add additional types as well as troubleshoot our code in the future.


masteringSwift.jpeg

Mastering Swift 5.3

The sixth edition of this bestselling book, updated to cover through version 5.3 of the Swift programming language

Amazon

Packt

pop.jpeg

Protocol Oriented Programming

Embrace the Protocol-Oriented design paradigm, for better code maintainability and increased performance, with Swift.

Amazon

Packt