In Coder Radio Episode 189, Mike & Chris discussed a video by Brian Will that highlighted the pain points in Object Oriented Programming. These problems, as I see them, can be broken down into a few key points. Code organization, shared state, and mutability.
During the show Mike had mentioned:
“Pure procedural code is freaking terrible”
Procedural code without structure sounds fairly terrifying indeed, but there is more to the world than just Object Oriented and plain procedural.
For those who might not be clued in on the hubbub, let’s go quickly go over what can differentiate pure procedural code from Object Oriented code.
Pure procedural code is a bit like writing a recipe.
Please do this first thing. Please do this second thing. All done, have a good day.
This style works for small scripts and snippets but gets far too hairy far too quickly for large projects.
Object Oriented code organizes the work into objects and classes. Most modern platforms require you to put your code into some object. At some level, your objects contain bits of procedural code that are hopefully broken down in a meaningful way.
Make sense? Good.
All that out of the way, I think the next logical progression for popular programming won’t be a foray into pure procedural but an exploration of Protocol Oriented Programming.
What is Protocol Oriented Programming? I’m glad you asked. Put in plain terms, instead of putting your code into classes, subclasses, and making objects, you outline your requirements. You put those requirements into a Protocol. After that, objects and structs can declare that they conform to that protocol and meet all the requirements. You write your code assuming whatever bits you throw at it fit your protocols and then you can plug and play pieces as needed.
In Object Oriented programming, you might need to inherit all the cruft from another class so you can plug in a new bit of code. Worse yet, some programming languages do not support multiple inheritance. Meaning you can only inherit from one class!
Protocol Oriented Programming isn’t just all theory. Swift and Objective-C Developers have had protocols for quite a while now, and WWDC 2015 had a good session on how to get started using it in your code.
Mike goes on to say:
“Now, Sharing global state is generally bad. But let me give you a common case that’s, you know, in just about everything I’ve ever written where it’s good. You need to have some sort of global session object for your iOS app… Well, in that case, something that is also bad called a Singleton probably makes sense, right? A Singleton user for the current user to save things like his picture, if it’s a social thing, or his username, or whatever crap that you need all over the place… And, oh you’ve created a global. You’ve created a, you know, you’ve created an object. It’s a singleton that’s never released. But The practical maintainability of just giving in and doing that versus: I’m going to do all of these weird, you know, mathematically perfect but really contorted ways of to avoid having shared state or global state… It just doesn’t make sense.”
For those out of the loop, a Singleton is an object that is only ever made to be initialized once. They are designed assuming that there can be only one of their kind running at a time. In iOS projects, it is common for Singletons to be made globally available from anywhere in your code. They can be seen as kind of a shared scratchpad.
The need for Singletons seems apparent. You want to save stuff and find it later, but you need to do it everywhere. But having this object available to a bunch of other objects means that there many places for a poorly-written object to go crazy and mangle this global state. This can lead to some unintended and rather unruly consequences.
The perfect, shiny, Protocol Oriented solution to this is to minimize where the globals are in your code and what you do with them.
Maybe the state of the user's information is stored in CoreData, a SQL database, a flat file, or even User Preferences and you need a shared object to deal with your data storage due to the nature of the beast.
In our potential, perfect, protocol world, you can define an object to be initialized in your classes or View Controllers as needed. This shiny object safely talks to the one Singleton object that manages interactions with your storage instead of every view controller having a reference to, and an opportunity to mess with, something that is important and needs to be shared. This means that only a series of approved functions can write to our shared scratchpad.
See Andy Matuschak’s talk at NSSpain for more on this.
“At some point You need data structures that are, you know… Maybe object is the wrong word… You need a data structure… Well no, that’s an object.”
No one remembers structs. Structs never get enough love.
I should explain the case of Objects v. Classes before going on. Objects and structs are two separate things that exist in a lot of languages.
Objects are reference types. They reference information in memory or on disk. They point to the information, they contain the information, but they are not the information.
Like a box with a label that tells you what to expect inside.
A box can hold a tennis ball but the box is not a tennis ball. Objects are also are mutable by default in many languages. This means that the information they point to can change. Someone can easily change what’s in the box to something else. Mutating information you’re referencing can be useful because sometimes you need to change what’s in the box.
But sometimes it isn’t, because what’s in the box changes to something you weren’t expecting.
Structs are value types. Value types are the information directly. They do not refer to something. They are like the tennis ball itself. You can easily compare two value types. In Swift, you just need to make your struct conform to the
Equatable protocol. You cannot easily change the tennis ball into something else. You need to get a different tennis ball.
Value types in Swift can have any of the claimed advantages of reference types now. They can inherit from other value types. Instead of inheritance, you can use extensions to your value types. For Polymorphism, structs have protocols now so that is covered. Encapsulation, as stated in the Brian Will video, can get very silly. Now that we have languages with actual namespaces we don’t need to put something in a class to encapsulate it. If you want a horror story, look at any GitHub project written in Java from a large company with 1,000,000,000 folders and 3 files.
Structs and value types also have the additional advantage of getting rid of shared references. If two parts of your code point to an object, one reference could mutate the object in a way that the other reference doesn’t expect. You can’t do that with structs. In Swift, structs can only be modified by methods that explicitly say they will mutate the struct.
For these advantages, you see the Swift team at Apple moving more and more bits out of objects and into structs. Errors in Swift are no longer NSError objects; they’re ErrorType structs with extensions. Strings are no longer NSString objects; They’re value types now.
And it is not just Apple. Other languages are backing away from reference types being the default way of programmers dealing with data. Rust uses explicit ownership and borrowing instead of shared references. Go has some devil magic with classes just being made out of functions that I still don’t understand.
There is a great WWDC session available on how to move over to using more and more value types instead of reference types in your code.
Object Oriented may not be dying quickly, but OO as we know it today is starting to feel more and more like a relic from the time it was created. Making developers manage the intricacies of references when many could not care less. The Mad Max world of anything goes message passing and reference types all over the runtime feels like something that could only have come from a group of 1980’s, over the top, Smalltalk fans. Maybe what we really need is some protocol, structure, and value. 🕶