Design Patterns in Swift: Abstract Factory

      No Comments on Design Patterns in Swift: Abstract Factory

Here we'll cover the swift version of the text-book version of the design pattern and say a few words about the dependency inversion principle.

Abstract factory is one of the creational design patterns. You probably encountered it in your development without realising. Here we’ll cover the swift version of the text-book version of the design pattern and say a few words about the dependency inversion principle.

Abstract Factory

We’re all taught to code to an interface, not an implementation. And that’s some really good advice. But eventually you’ll have to create some objects somewhere. That’s where the abstract factory pattern comes in.

The purpose of the abstract factory is to abstract away object creation and centralise it in a class. This way you can keep coding to an interface, and inject your factory into the classes that need to use it. The actual definition of the abstract factory pattern from the gang of four is:

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

As the definition says, the abstract factory is nothing more than a protocol that we’ll use on a concrete class to create objects. In our example we’ll use the abstract factory to create some animal shops. This will be a simple factory, let’s see it on a diagram first:

Let’s see how to implement this pattern in swift.

The Code

We’ll only need three protocols to implement this factory, the animal shop protocol:

protocol AnimalShop {
    func createDog() -> Dog?
    func createCat() -> Cat?
}

This will be our factory protocol. ‘Abstract’ is a keyword in other languages that we don’t have in swift/obj-c, the closest keyword we have would be ‘protocol’. So this is our abstract factory definition. The factory will create a dog and a cat, so we need protocols for them as well:

protocol Dog {
    var name: String { get }
    func bark()
}
protocol Cat {
    var name: String { get }
    func meow()
}

We’ll be making three factories. For creating pets, toys, and a mock factory for out unit tests.

PetShop Factory

Let’s say you’re creating a pet store, you might have a factory that looks something like this:

class PetShop: AnimalShop {
    
    private let dict: [String: String]
    
    init(dict: [String: String]) {
        self.dict = dict
    }

    func createDog() -> Dog? {
        guard let name = dict["dogName"] else {
            return nil
        }
        return PetShopDog(name: name)
    }
    
    func createCat() -> Cat? {
        guard let name = dict["catName"] else {
            return nil
        }
        return PetShopCat(name: name)
    }
}

private struct PetShopDog: Dog {
    var name: String
    
    func bark() {
        print("\(name) is barking!")
    }
}

private struct PetShopCat: Cat {
    var name: String
    
    func meow() {
        print("\(name) doesn't care")
    }
}

This factory takes in a dictionary with a dog and a cat name, and it creates your cats and dogs out of that dictionary. You probably noticed that the ‘PetShopDog’ and ‘PetShopCat’ structs are private and accessible only to the ‘PetShop’ factory. Obviously, in these classes you would implement your real objects with their custom behaviour. Here we just have some cats and some dogs.

ToyShop Factory

Pet shop is not the only shop that can sell cats and dogs. We can have totally different types of objects being created that are still considered to be cats and dogs. Let’s create a simple toy shop factory:

class ToyShop: AnimalShop {
    
    private let inventory: [String: Any]
    
    init(inventory: [String: Any]) {
        self.inventory = inventory
    }
    
    func createDog() -> Dog? {
        guard
            let dict = inventory["dog"] as? [String: String],
            let name = dict["name"]
            else {
                return nil
        }
        
        return ToyDog(name: name)
    }
    
    func createCat() -> Cat? {
        guard
            let dict = inventory["cat"] as? [String: String],
            let name = dict["name"]
            else {
                return nil
        }
        
        return ToyCat(name: name)
    }
}

private struct ToyDog: Dog {
    var name: String
    
    func bark() {
        print("Put some batteries in first")
    }
}

private struct ToyCat: Cat {
    var name: String
    
    func meow() {
        print("press my paw to meow")
    }
}

Here our factory is being initialised with a different type of object, a dictionary of dictionaries. And, as you can see, the actual ‘ToyDog’ and ‘ToyCat’ implementations are different from the pet shop. A toy dog will need batteries to bark 🙂

MockShop Factory

If you’re writing your unit tests, and I hope you are, you’ll want to be able to create your cats and dogs in a controlled manner. Mock factories are ideal for this. You can inject these factories into your classes and be sure the generated objects are exactly the ones you would expect. An example of a mock factory would be:

class MockShop: AnimalShop {
    func createDog() -> Dog? {
        return MockDog(name: "Fidisimo")
    }
    
    func createCat() -> Cat? {
        return MockCat(name: "Bender")
    }
}

private struct MockDog: Dog {
    var name: String
    
    func bark() {
        print("bark yourself")
    }
}

private struct MockCat: Cat {
    var name: String
    
    func meow() {
        print("kill all humans")
    }
}

This is by far the simplest factory so far, but that’s exactly what you want. Why complicate things for yourself 🙂 You would normally use this kind of factory in a unit test.

Dependency Inversion Principle

Throughout this example we actually used the dependency inversion principle. The definition of it would go something like:

Depend on abstractions, don’t depend on concrete classes.

If you’re wondering what all of this means, just look at any of the factories we created. We’re implementing a factory that’s creating a dog object, but we’re keeping the concrete class implementation private, thus forcing the use of dependency inversion principle. All of the properties and methods that we need are in the ‘Dog’ protocol, and the rest of our app can use that dog object without any problems, we’re just keeping the actual concrete implementation of our dog hidden.

We all know that we should code to an interface, not an implementation, well, dependency inversion is just an extreme way of doing that. An easy way of remembering to use it is, always use the methods in a protocol, if your methods/properties are not in the protocol, you’re breaking the principle.

Conclusion

In this article we covered the text-book version of the abstract factory. I say ‘text book’ because I don’t really use this exact version of the pattern in my day-to-day development. What I end up using is a simple abstract factory. I rarely use a factory to create a family of items, i just need it to create one item, or an array of items of the same type.

Apart from that small deviation, I usually follow the pattern, and it works out pretty well. Creating mock objects is one obvious benefit. One other great benefit would be if you end up changing our API from XML to JSON, you can do it pretty seamlessly with the factory pattern. You just create a factory that will create your objects from the JSON response. One other example where I benefited from this pattern was when we changed the structure of our API responses for one of the calls. We had a new API endpoint, and the only thing we needed to do was create a new factory for the response object.

Abstract factory is a great pattern to master, and it’s pretty simple too. What it will do is force you to adhere to the dependency inversion principle, which is a lot more powerful than you might think. If you start using these two, your code will be a lot more flexible and you will save yourself a lot of trouble in the future.

The code for this little exercise is in the text, I didn’t commit it to my GitHub account. I hope you found something useful in this article and I wish you happy coding.

Have a nice day 🙂

Dejan.

More resources

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.