2 Design Principles I Personally Use for iOS Projects
Practical application of Open-Closed Principle and Dependency Inversion Principle
I’ll admit, sometimes when I look back at code that I wrote 2 years ago, I get confused myself. Or worse, when I want modify one part of the codebase and I realise that I have to change everything else because my codebase’s just a mess.
If you can relate, this article is for you. Software Design principles are important because programming languages are not just for humans to speak to computers. When we code, we want to be able to communicate our ideas to other humans as well. With a properly segmented codebase and minimal unnecessary dependencies, we can spend less time trying to find bugs and focus on what really matters.
In my previous article, I wrote about MVC vs MVVM in the iOS context and we developed a slick PetManager app. But there was a major issue. It wasn’t really a PetManager because it was only able to manage Cats. Honestly, that’d be alright for me but I’m sure there are many dog and hamster lovers out there. So in this article, we’ll take a look at using certain design principles to make our app more flexible in accommodating such changes.
You can check out the code at https://github.com/wilfredbtan/PetManager and checkout to the “principles-base” branch. If you want to go straight to the finished product, you can checkout the “principles-solution” branch as well.
Here’s the before (left) and after (right) of what the app should look like:
And here’s what our current model looks like:
If you’ve read my previous article, you’ll notice that our app now keeps track of our cat’s statuses (unsatisfied or meowing). But the issue arises when I suddenly gain an interest in dogs. Having meowing dogs would be cool but that’s not the point of this article.
Now let’s try adding Dogs to our model:
Yay it works! Ok not really. Now let’s say I want to add hamsters and fishes into my collection. Responsible pet ownership aside, it will basically make the codebase a mess. A major issue is that we have to keep modifying the PetManager class and that smells of bad programming to me.
This is where we can apply the Open-Closed Principle and the Dependency Inversion Principle.
Open-Closed Principle and Dependency Inversion Principle
The Open-Closed Principle (OCP) states that “Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”. The reason why I have to change the internal implementation of the app is because the PetManager is directly dependent on lower-level modules such as Cat and Dog. This leads me to the next interface which is the Dependency Inversion Principle (DIP) which states that “High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g. interfaces).”
If we look at the code now, it seems like we need to create a new satisfyDog() and getDogResponse() if we add a dog class.
But that would violate OCP. In order to add a new type of pet without violating OCP, we can apply the DIP. Instead of letting PetManager (high-level) rely on Cat or Dog (low-level) modules. Both of them can rely on an abstraction (e.g. a PetResponse interface).
Here’s what it’ll look like:
We see that by inverting the dependency, we have also satisfied OCP as we can now add more ___Responses without modifying the PetManagerModel class. But what about satisfying the pets? (i.e. satisfyCat() and satisfyDog()). By having both classes implement a Pet interface, we can simply have both implement the satisfy() method and the PetManagerModel will only need to call that.
After applying OCP and DIP, our code base will look something like this:
And we did it, we now have an application that is reusable and extendible as well.
It may seem like an overkill for a small application such as this but just imagine having to modify existing class every time a change is needed. In bigger applications, the likelihood of causing regressions and bugs is much higher.
Many of you may also have realised that OCP and DIP are just a subset of the SOLID principles which was coined by Michael Feathers but first introduced by Robert C. Martin aka Uncle Bob. I really recommend reading up on those principles as it’ll help you get started on your road to having better programming practices.
This article mainly used examples in Swift but these principles can definitely be applied in other languages as well. Try playing around with the app and find out more ways to make your code more flexible. For example, what if I wanted to change the theme of the app depending on whether it is a Cat or a Dog? How do I support the addition of such a theme without violating the principles mentioned above? Let me know how this article has helped you in the comments below :D