Modules and Change Control: Why I’m Excited About Protocol Oriented Programming in Swift
Have you ever lived without a refrigerator? Well, that’s what I’ve been doing since ours broke more than a month ago. Turns out, our kitchen was renovated around our fridge, so getting the broken one out of there is a nightmare. The whole experience has gotten me thinking about how much we rely on our appliances to do different things for us.
When we first got that refrigerator, I was really excited because it also had a water dispenser. So now, our fridge wasn’t just for storing food and keeping it cold, it was a place to get water too. We already had a water dispenser, our kitchen sink. We use that for washing dishes, though, for which we also have a dishwasher. We have a lot of different appliances, doing the same thing in different ways, to accomplish various tasks.
As a novice programmer, I’m always thinking about how I’d represent things in code. In general, when I’m writing code, my philosophy is to ask “What does it look like to accomplish this task? What are the mechanics and the steps involved? What are the expected conditions and results?” So when I’m learning about all the things I can do with, say, Swift, I make analogies to how I’d use them to solve problems in life.
So I have a kitchen full of appliances and I want to represent them in code. Traditionally with Object Oriented Programming, I would define a class for each general appliance type and then have specific subclasses to share most of its methods and properties and add their own unique ones. That’s great, but here’s the thing: like a program, my kitchen has gone through numerous iterations. If I was coding my kitchen in December (I’ll call this previous version Kitchen 2.0), because my refrigerator dispenses water and was also the only thing that kept food cold, maybe I would have defined the Sink class first, then had the fridge include a Sink object to allow it to dispense water. I would have had the dishwasher inherit the ability to wash dishes, but not dispense water.
Well, now the fridge is broken and we have a mini-fridge. That doesn’t dispense water at all. So if I was updating the code for my kitchen, I’d have to make a new class for my mini fridge that doesn’t inherit the water dispensing. Well whatever, that’s just the nature of big projects like this.
What happens when we get our new fridge, though? Technology has advanced a lot in 10 years and now there’s an entirely new class of appliance, “smart appliances”, that can access the internet. In the OOP paradigm, going from the renovated Kitchen 2.o to the smart appliance Kitchen 3.o would be an insane amount of work. I would have to decide whether to re-class almost everything, making my fridge the parent class for smart appliances and cooling devices and whether I want it to inherit from the mini-fridge or become its parent, or wether to create a new smart appliance object and include that in my classes.
When I change a parent class, I have to go back and make sure my changes haven’t broken any of the child classes. I have to go to each one of the child classes and make sure I’m accounting for any changes to the parent class code in their internal methods, and make sure I’m overriding any methods I don’t want to inherit or want to function differently. This whole process is going to take time away from implementing the cool “smart appliance” features I’m actually excited about. The Kitchen 3.o update is looking to be a huge nightmare.
Additionally, you can see how inheritance stands its own conceptual basis on its head. Normally, you would imagine a simple object inheriting its functions from a more complex one. However the Inheritance model has more complex objects inherit from simple ones. This causes changes to filter outward from objects that are simple and easy to fix into objects that are complex and more difficult to fix.
This is why I’m so excited about Protocol Oriented Programming. Protocols were intended to outline requirements for Objects so that they can interact with other Objects safely. But with my current kitchen situation, I can see how the versatility of POP would really be helpful in actually accomplishing the goals OOP set out to achieve. I could do that by switching over from a traditional Inheritance model to a Modular (Aspect-Oriented) model using Swift’s Protocols. And it would make Kitchen 3.o a cinch!
In this Protocol centered model, I could divide all the common methods I needed available to multiple classes into Protocols. Protocols will allow me to define functions I need to be common to several classes, and implement requirements for variables needed to operate them. Below, you can see how I’m implementing my Container module in Swift using a protocol. I’m using “_m” to differentiate my modules from other kinds of protocols. You can see that I’m setting a requirement for a variable, “storedItems”. I need to define whether this variable will be directly accessible and, in case I am conforming Structs to this module, I need to define methods that modify them as Mutating.
In order to allow a class to use the module, I need to add it to the class declaration just like a normal protocol. Now, any instance of the class can use the methods it gains from the module. I don’t have to declare these methods in order to have my code compile, or override them if I need to add functionality or replace them entirely. I can create another function with the same name to add additional functionality if I want to. Below, you can see how I’ve implemented a Refrigerator class from Kitchen 2.0 with the Container and WaterDispenser modules.
With my ability to create modules, I can establish all the other methods that were previously inherited in protocols for “dish washer”, “cooker”, and “chiller”. I can combine all of these in different permutations, hooking them onto small classes or structs that have the unique properties of each appliance. And when I want to add a new appliance, I just list all the things it can do! Now when I make changes, they will only take place in the protocols. Even in the most complex objects, I only need to look for errors where an object modifies or calls one of its module’s functions. I can separate necessary and unnecessary functionality by splitting modules and adding them where they are needed.
So I get my new fridge. I add the “chiller” protocol, the “container” protocol, and the “water dispenser” protocol. In the class functions I crank up the container and chiller properties because it’s huge and keeps things cold better than the mini-fridge we’re using. I crank down the water dispenser features because I’m not gonna use it to fill a spaghetti pot. And my only big job for the Kitchen 3.o update is to add the new smart appliance features to a new “smart appliance” protocol. That way in Kitchen 3.0, when I get my Smart Microwave, I can add it in with no problem!
I’m not a professional but I can see the benefits on the business end too. Because I’m focusing my time on adding the new features, I don’t have to spend a ton of time re-classing everything. Rather than having to pore over my entire kitchen code making sure all the changes I’ve made to the class structure haven’t introduced a swarm of new bugs, I can focus on making the new “smart appliance” feature the best it can possibly be!
So, that’s why I think Swift’s protocols will make inheritance versatile and version control painless. I’m all for the new changes to Swift and I’m really excited to use it to think about solving problems in a new way.